@NotNull
  private static Couple<Collection<TextRange>> getUsages(
      @NotNull PsiElement target,
      PsiElement psiElement,
      boolean withDeclarations,
      boolean detectAccess) {
    List<TextRange> readRanges = new ArrayList<TextRange>();
    List<TextRange> writeRanges = new ArrayList<TextRange>();
    final ReadWriteAccessDetector detector =
        detectAccess ? ReadWriteAccessDetector.findDetector(target) : null;
    final FindUsagesManager findUsagesManager =
        ((FindManagerImpl) FindManager.getInstance(target.getProject())).getFindUsagesManager();
    final FindUsagesHandler findUsagesHandler =
        findUsagesManager.getFindUsagesHandler(target, true);
    final LocalSearchScope scope = new LocalSearchScope(psiElement);
    Collection<PsiReference> refs =
        findUsagesHandler != null
            ? findUsagesHandler.findReferencesToHighlight(target, scope)
            : ReferencesSearch.search(target, scope).findAll();
    for (PsiReference psiReference : refs) {
      if (psiReference == null) {
        LOG.error(
            "Null reference returned, findUsagesHandler="
                + findUsagesHandler
                + "; target="
                + target
                + " of "
                + target.getClass());
        continue;
      }
      List<TextRange> destination;
      if (detector == null
          || detector.getReferenceAccess(target, psiReference)
              == ReadWriteAccessDetector.Access.Read) {
        destination = readRanges;
      } else {
        destination = writeRanges;
      }
      HighlightUsagesHandler.collectRangesToHighlight(psiReference, destination);
    }

    if (withDeclarations) {
      final TextRange declRange =
          HighlightUsagesHandler.getNameIdentifierRange(psiElement.getContainingFile(), target);
      if (declRange != null) {
        if (detector != null && detector.isDeclarationWriteAccess(target)) {
          writeRanges.add(declRange);
        } else {
          readRanges.add(declRange);
        }
      }
    }

    return Couple.<Collection<TextRange>>of(readRanges, writeRanges);
  }
 @NotNull
 private static FindUsagesOptions getDefaultOptions(@NotNull FindUsagesHandler handler) {
   FindUsagesOptions options =
       handler.getFindUsagesOptions(DataManager.getInstance().getDataContext());
   // by default, scope in FindUsagesOptions is copied from the FindSettings, but we need a default
   // one
   options.searchScope = FindUsagesManager.getMaximalScope(handler);
   return options;
 }
 private void showDialogAndFindUsages(
     @NotNull FindUsagesHandler handler,
     @NotNull RelativePoint popupPosition,
     Editor editor,
     int maxUsages) {
   AbstractFindUsagesDialog dialog = handler.getFindUsagesDialog(false, false, false);
   dialog.show();
   if (dialog.isOK()) {
     dialog.calcFindUsagesOptions();
     showElementUsages(handler, editor, popupPosition, maxUsages, getDefaultOptions(handler));
   }
 }
 @Nullable
 private static String getSecondInvocationTitle(
     @NotNull FindUsagesOptions options, @NotNull FindUsagesHandler handler) {
   if (getShowUsagesShortcut() != null) {
     GlobalSearchScope maximalScope = FindUsagesManager.getMaximalScope(handler);
     if (!notNullizeScope(options, handler.getProject()).equals(maximalScope)) {
       return "Press "
           + KeymapUtil.getShortcutText(getShowUsagesShortcut())
           + " again to search in "
           + maximalScope.getDisplayName();
     }
   }
   return null;
 }
 private void navigateAndHint(
     @NotNull Usage usage,
     @Nullable final String hint,
     @NotNull final FindUsagesHandler handler,
     @NotNull final RelativePoint popupPosition,
     final int maxUsages,
     @NotNull final FindUsagesOptions options) {
   usage.navigate(true);
   if (hint == null) return;
   final Editor newEditor = getEditorFor(usage);
   if (newEditor == null) return;
   final Project project = handler.getProject();
   // opening editor is performing in invokeLater
   IdeFocusManager.getInstance(project)
       .doWhenFocusSettlesDown(
           new Runnable() {
             @Override
             public void run() {
               newEditor
                   .getScrollingModel()
                   .runActionOnScrollingFinished(
                       new Runnable() {
                         @Override
                         public void run() {
                           // after new editor created, some editor resizing events are still
                           // bubbling. To prevent hiding hint, invokeLater this
                           IdeFocusManager.getInstance(project)
                               .doWhenFocusSettlesDown(
                                   new Runnable() {
                                     @Override
                                     public void run() {
                                       if (newEditor.getComponent().isShowing()) {
                                         showHint(
                                             hint,
                                             newEditor,
                                             popupPosition,
                                             handler,
                                             maxUsages,
                                             options);
                                       }
                                     }
                                   });
                         }
                       });
             }
           });
 }
  private void showElementUsages(
      @NotNull final FindUsagesHandler handler,
      final Editor editor,
      @NotNull final RelativePoint popupPosition,
      final int maxUsages,
      @NotNull final FindUsagesOptions options) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    final UsageViewSettings usageViewSettings = UsageViewSettings.getInstance();
    final UsageViewSettings savedGlobalSettings = new UsageViewSettings();

    savedGlobalSettings.loadState(usageViewSettings);
    usageViewSettings.loadState(myUsageViewSettings);

    final Project project = handler.getProject();
    UsageViewManager manager = UsageViewManager.getInstance(project);
    FindUsagesManager findUsagesManager =
        ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager();
    final UsageViewPresentation presentation =
        findUsagesManager.createPresentation(handler, options);
    presentation.setDetachedMode(true);
    final UsageViewImpl usageView =
        (UsageViewImpl)
            manager.createUsageView(UsageTarget.EMPTY_ARRAY, Usage.EMPTY_ARRAY, presentation, null);

    Disposer.register(
        usageView,
        new Disposable() {
          @Override
          public void dispose() {
            myUsageViewSettings.loadState(usageViewSettings);
            usageViewSettings.loadState(savedGlobalSettings);
          }
        });

    final List<Usage> usages = new ArrayList<Usage>();
    final Set<UsageNode> visibleNodes = new LinkedHashSet<UsageNode>();
    UsageInfoToUsageConverter.TargetElementsDescriptor descriptor =
        new UsageInfoToUsageConverter.TargetElementsDescriptor(
            handler.getPrimaryElements(), handler.getSecondaryElements());

    final MyTable table = new MyTable();
    final AsyncProcessIcon processIcon = new AsyncProcessIcon("xxx");
    boolean hadMoreSeparator = visibleNodes.remove(MORE_USAGES_SEPARATOR_NODE);
    if (hadMoreSeparator) {
      usages.add(MORE_USAGES_SEPARATOR);
      visibleNodes.add(MORE_USAGES_SEPARATOR_NODE);
    }

    addUsageNodes(usageView.getRoot(), usageView, new ArrayList<UsageNode>());

    TableScrollingUtil.installActions(table);

    final List<UsageNode> data = collectData(usages, visibleNodes, usageView, presentation);
    setTableModel(table, usageView, data);

    SpeedSearchBase<JTable> speedSearch = new MySpeedSearch(table);
    speedSearch.setComparator(new SpeedSearchComparator(false));

    final JBPopup popup =
        createUsagePopup(
            usages,
            descriptor,
            visibleNodes,
            handler,
            editor,
            popupPosition,
            maxUsages,
            usageView,
            options,
            table,
            presentation,
            processIcon,
            hadMoreSeparator);

    Disposer.register(popup, usageView);

    // show popup only if find usages takes more than 300ms, otherwise it would flicker needlessly
    Alarm alarm = new Alarm(usageView);
    alarm.addRequest(
        new Runnable() {
          @Override
          public void run() {
            showPopupIfNeedTo(popup, popupPosition);
          }
        },
        300);

    final PingEDT pingEDT =
        new PingEDT(
            "Rebuild popup in EDT",
            new Condition<Object>() {
              @Override
              public boolean value(Object o) {
                return popup.isDisposed();
              }
            },
            100,
            new Runnable() {
              @Override
              public void run() {
                if (popup.isDisposed()) return;

                final List<UsageNode> nodes = new ArrayList<UsageNode>();
                List<Usage> copy;
                synchronized (usages) {
                  // open up popup as soon as several usages 've been found
                  if (!popup.isVisible()
                      && (usages.size() <= 1 || !showPopupIfNeedTo(popup, popupPosition))) {
                    return;
                  }
                  addUsageNodes(usageView.getRoot(), usageView, nodes);
                  copy = new ArrayList<Usage>(usages);
                }

                rebuildPopup(
                    usageView,
                    copy,
                    nodes,
                    table,
                    popup,
                    presentation,
                    popupPosition,
                    !processIcon.isDisposed());
              }
            });

    final MessageBusConnection messageBusConnection = project.getMessageBus().connect(usageView);
    messageBusConnection.subscribe(
        UsageFilteringRuleProvider.RULES_CHANGED,
        new Runnable() {
          @Override
          public void run() {
            pingEDT.ping();
          }
        });

    Processor<Usage> collect =
        new Processor<Usage>() {
          private final UsageTarget[] myUsageTarget = {
            new PsiElement2UsageTargetAdapter(handler.getPsiElement())
          };

          @Override
          public boolean process(@NotNull Usage usage) {
            synchronized (usages) {
              if (!filter.shouldShow(usage)) return true;
              if (visibleNodes.size() >= maxUsages) return false;
              if (UsageViewManager.isSelfUsage(usage, myUsageTarget)) {
                return true;
              }

              Usage usageToAdd = transform(usage);
              if (usageToAdd == null) return true;

              UsageNode node = usageView.doAppendUsage(usageToAdd);
              usages.add(usageToAdd);
              if (node != null) {
                visibleNodes.add(node);
                boolean continueSearch = true;
                if (visibleNodes.size() == maxUsages) {
                  visibleNodes.add(MORE_USAGES_SEPARATOR_NODE);
                  usages.add(MORE_USAGES_SEPARATOR);
                  continueSearch = false;
                }
                pingEDT.ping();

                return continueSearch;
              }
              return true;
            }
          }
        };

    final ProgressIndicator indicator =
        FindUsagesManager.startProcessUsages(
            handler,
            handler.getPrimaryElements(),
            handler.getSecondaryElements(),
            collect,
            options,
            new Runnable() {
              @Override
              public void run() {
                ApplicationManager.getApplication()
                    .invokeLater(
                        new Runnable() {
                          @Override
                          public void run() {
                            Disposer.dispose(processIcon);
                            Container parent = processIcon.getParent();
                            parent.remove(processIcon);
                            parent.repaint();
                            pingEDT.ping(); // repaint title
                            synchronized (usages) {
                              if (visibleNodes.isEmpty()) {
                                if (usages.isEmpty()) {
                                  String text =
                                      UsageViewBundle.message(
                                          "no.usages.found.in",
                                          searchScopePresentableName(options, project));
                                  showHint(
                                      text, editor, popupPosition, handler, maxUsages, options);
                                  popup.cancel();
                                } else {
                                  // all usages filtered out
                                }
                              } else if (visibleNodes.size() == 1) {
                                if (usages.size() == 1) {
                                  // the only usage
                                  Usage usage = visibleNodes.iterator().next().getUsage();
                                  usage.navigate(true);
                                  // String message =
                                  // UsageViewBundle.message("show.usages.only.usage",
                                  // searchScopePresentableName(options, project));
                                  // navigateAndHint(usage, message, handler, popupPosition,
                                  // maxUsages, options);
                                  popup.cancel();
                                } else {
                                  assert usages.size() > 1 : usages;
                                  // usage view can filter usages down to one
                                  Usage visibleUsage = visibleNodes.iterator().next().getUsage();
                                  if (areAllUsagesInOneLine(visibleUsage, usages)) {
                                    String hint =
                                        UsageViewBundle.message(
                                            "all.usages.are.in.this.line",
                                            usages.size(),
                                            searchScopePresentableName(options, project));
                                    navigateAndHint(
                                        visibleUsage,
                                        hint,
                                        handler,
                                        popupPosition,
                                        maxUsages,
                                        options);
                                    popup.cancel();
                                  }
                                }
                              } else {
                                String title = presentation.getTabText();
                                boolean shouldShowMoreSeparator =
                                    visibleNodes.contains(MORE_USAGES_SEPARATOR_NODE);
                                String fullTitle =
                                    getFullTitle(
                                        usages,
                                        title,
                                        shouldShowMoreSeparator,
                                        visibleNodes.size() - (shouldShowMoreSeparator ? 1 : 0),
                                        false);
                                ((AbstractPopup) popup).setCaption(fullTitle);
                              }
                            }
                          }
                        },
                        project.getDisposed());
              }
            });
    Disposer.register(
        popup,
        new Disposable() {
          @Override
          public void dispose() {
            indicator.cancel();
          }
        });
  }
  public static List<ParameterInfoImpl> performChange(
      final Project project,
      final Editor editor,
      final PsiFile file,
      final PsiMethod method,
      final int minUsagesNumber,
      final ParameterInfoImpl[] newParametersInfo,
      final boolean changeAllUsages,
      final boolean allowDelegation) {
    if (!FileModificationService.getInstance().prepareFileForWrite(method.getContainingFile()))
      return null;
    final FindUsagesManager findUsagesManager =
        ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager();
    final FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(method, false);
    if (handler == null) return null; // on failure or cancel (e.g. cancel of super methods dialog)

    final JavaMethodFindUsagesOptions options = new JavaMethodFindUsagesOptions(project);
    options.isImplementingMethods = true;
    options.isOverridingMethods = true;
    options.isUsages = true;
    options.isSearchForTextOccurrences = false;
    final int[] usagesFound = new int[1];
    Runnable runnable =
        () -> {
          Processor<UsageInfo> processor = t -> ++usagesFound[0] < minUsagesNumber;

          handler.processElementUsages(method, processor, options);
        };
    String progressTitle = QuickFixBundle.message("searching.for.usages.progress.title");
    if (!ProgressManager.getInstance()
        .runProcessWithProgressSynchronously(runnable, progressTitle, true, project)) return null;

    if (ApplicationManager.getApplication().isUnitTestMode() || usagesFound[0] < minUsagesNumber) {
      ChangeSignatureProcessor processor =
          new ChangeSignatureProcessor(
              project,
              method,
              false,
              null,
              method.getName(),
              method.getReturnType(),
              newParametersInfo) {
            @Override
            @NotNull
            protected UsageInfo[] findUsages() {
              return changeAllUsages ? super.findUsages() : UsageInfo.EMPTY_ARRAY;
            }

            @Override
            protected void performRefactoring(@NotNull UsageInfo[] usages) {
              CommandProcessor.getInstance().setCurrentCommandName(getCommandName());
              super.performRefactoring(usages);
            }
          };
      processor.run();
      ApplicationManager.getApplication().runWriteAction(() -> UndoUtil.markPsiFileForUndo(file));
      return Arrays.asList(newParametersInfo);
    } else {
      final List<ParameterInfoImpl> parameterInfos =
          newParametersInfo != null
              ? new ArrayList<ParameterInfoImpl>(Arrays.asList(newParametersInfo))
              : new ArrayList<ParameterInfoImpl>();
      final PsiReferenceExpression refExpr =
          JavaTargetElementEvaluator.findReferenceExpression(editor);
      JavaChangeSignatureDialog dialog =
          JavaChangeSignatureDialog.createAndPreselectNew(
              project, method, parameterInfos, allowDelegation, refExpr);
      dialog.setParameterInfos(parameterInfos);
      dialog.show();
      return dialog.isOK() ? dialog.getParameters() : null;
    }
  }