@Override
  public void performPreRunActivities(
      @NotNull final List<Tools> globalTools,
      @NotNull final List<Tools> localTools,
      @NotNull final GlobalInspectionContext context) {
    final AnalysisScope analysisScope = context.getRefManager().getScope();
    if (analysisScope == null) return;

    final GlobalSearchScope scope =
        GlobalSearchScope.EMPTY_SCOPE.union(analysisScope.toSearchScope());

    updateIndicator("Looking for Dart files...", -1);

    final Collection<VirtualFile> dartFiles =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<Collection<VirtualFile>>() {
                  @Override
                  public Collection<VirtualFile> compute() {
                    return FileTypeIndex.getFiles(DartFileType.INSTANCE, scope);
                  }
                });

    double index = 0.0;
    final int size = dartFiles.size();

    for (final VirtualFile dartFile : dartFiles) {
      index++;
      analyzeFile(dartFile, context.getProject(), index / size);
    }
  }
  public void findAllDeclarations() {
    if (!myDeclarationsFound) {
      long before = System.currentTimeMillis();
      final AnalysisScope scope = getScope();
      if (scope != null) {
        scope.accept(myProjectIterator);
      }
      myDeclarationsFound = true;

      LOG.info(
          "Total duration of processing project usages:" + (System.currentTimeMillis() - before));
    }
  }
  public void inspectionReadActionFinished() {
    myIsInProcess = false;
    if (myScope != null) myScope.invalidate();

    synchronized (myRefTable) {
      mySortedRefs = null;
    }
  }
  public static void invokeOnScope(
      final Project project,
      final Set<PsiMember> members,
      final AnalysisScope scope,
      boolean silent) {
    final Map<PsiMember, List<Match>> duplicates = new HashMap<>();
    final int fileCount = scope.getFileCount();
    final ProgressIndicator progressIndicator =
        ProgressManager.getInstance().getProgressIndicator();
    if (progressIndicator != null) {
      progressIndicator.setIndeterminate(false);
    }

    final Map<PsiMember, Set<Module>> memberWithModulesMap = new HashMap<>();
    for (final PsiMember member : members) {
      final Module module =
          ApplicationManager.getApplication()
              .runReadAction(
                  new Computable<Module>() {
                    @Override
                    public Module compute() {
                      return ModuleUtilCore.findModuleForPsiElement(member);
                    }
                  });
      if (module != null) {
        final HashSet<Module> dependencies = new HashSet<>();
        ApplicationManager.getApplication()
            .runReadAction(() -> ModuleUtilCore.collectModulesDependsOn(module, dependencies));
        memberWithModulesMap.put(member, dependencies);
      }
    }

    scope.accept(
        new PsiRecursiveElementVisitor() {
          private int myFileCount;

          @Override
          public void visitFile(final PsiFile file) {
            if (progressIndicator != null) {
              if (progressIndicator.isCanceled()) return;
              progressIndicator.setFraction(((double) myFileCount++) / fileCount);
              final VirtualFile virtualFile = file.getVirtualFile();
              if (virtualFile != null) {
                progressIndicator.setText2(
                    ProjectUtil.calcRelativeToProjectPath(virtualFile, project));
              }
            }
            final Module targetModule = ModuleUtilCore.findModuleForPsiElement(file);
            if (targetModule == null) return;
            for (Map.Entry<PsiMember, Set<Module>> entry : memberWithModulesMap.entrySet()) {
              final Set<Module> dependencies = entry.getValue();
              if (dependencies == null || !dependencies.contains(targetModule)) continue;

              final PsiMember method = entry.getKey();
              final List<Match> matchList = hasDuplicates(file, method);
              for (Iterator<Match> iterator = matchList.iterator(); iterator.hasNext(); ) {
                Match match = iterator.next();
                final PsiElement matchStart = match.getMatchStart();
                final PsiElement matchEnd = match.getMatchEnd();
                for (PsiMember psiMember : members) {
                  if (PsiTreeUtil.isAncestor(psiMember, matchStart, false)
                      || PsiTreeUtil.isAncestor(psiMember, matchEnd, false)) {
                    iterator.remove();
                    break;
                  }
                }
              }
              if (!matchList.isEmpty()) {
                List<Match> matches = duplicates.get(method);
                if (matches == null) {
                  matches = new ArrayList<>();
                  duplicates.put(method, matches);
                }
                matches.addAll(matchList);
              }
            }
          }
        });
    if (duplicates.isEmpty()) {
      if (!silent) {
        final Runnable nothingFoundRunnable =
            () -> {
              final String message =
                  RefactoringBundle.message(
                      "idea.has.not.found.any.code.that.can.be.replaced.with.method.call",
                      ApplicationNamesInfo.getInstance().getProductName());
              Messages.showInfoMessage(project, message, REFACTORING_NAME);
            };
        if (ApplicationManager.getApplication().isUnitTestMode()) {
          nothingFoundRunnable.run();
        } else {
          ApplicationManager.getApplication()
              .invokeLater(nothingFoundRunnable, ModalityState.NON_MODAL);
        }
      }
    } else {
      replaceDuplicate(project, duplicates, members);
    }
  }
  public DependenciesPanel(
      Project project, final List<DependenciesBuilder> builders, final Set<PsiFile> excluded) {
    super(new BorderLayout());
    myBuilders = builders;
    myExcluded = excluded;
    final DependenciesBuilder main = myBuilders.get(0);
    myForward = !main.isBackward();
    myScopeOfInterest = main.getScopeOfInterest();
    myTransitiveBorder = main.getTransitiveBorder();
    myDependencies = new HashMap<PsiFile, Set<PsiFile>>();
    myIllegalDependencies = new HashMap<PsiFile, Map<DependencyRule, Set<PsiFile>>>();
    for (DependenciesBuilder builder : builders) {
      myDependencies.putAll(builder.getDependencies());
      myIllegalDependencies.putAll(builder.getIllegalDependencies());
    }
    exclude(excluded);
    myProject = project;
    myUsagesPanel = new DependenciesUsagesPanel(myProject, myBuilders);
    Disposer.register(this, myUsagesPanel);

    final Splitter treeSplitter = new Splitter();
    Disposer.register(
        this,
        new Disposable() {
          public void dispose() {
            treeSplitter.dispose();
          }
        });
    treeSplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myLeftTree));
    treeSplitter.setSecondComponent(ScrollPaneFactory.createScrollPane(myRightTree));

    final Splitter splitter = new Splitter(true);
    Disposer.register(
        this,
        new Disposable() {
          public void dispose() {
            splitter.dispose();
          }
        });
    splitter.setFirstComponent(treeSplitter);
    splitter.setSecondComponent(myUsagesPanel);
    add(splitter, BorderLayout.CENTER);
    add(createToolbar(), BorderLayout.NORTH);

    myRightTreeExpansionMonitor = PackageTreeExpansionMonitor.install(myRightTree, myProject);
    myLeftTreeExpansionMonitor = PackageTreeExpansionMonitor.install(myLeftTree, myProject);

    myRightTreeMarker =
        new Marker() {
          public boolean isMarked(VirtualFile file) {
            return myIllegalsInRightTree.contains(file);
          }
        };

    myLeftTreeMarker =
        new Marker() {
          public boolean isMarked(VirtualFile file) {
            return myIllegalDependencies.containsKey(file);
          }
        };

    updateLeftTreeModel();
    updateRightTreeModel();

    myLeftTree
        .getSelectionModel()
        .addTreeSelectionListener(
            new TreeSelectionListener() {
              public void valueChanged(TreeSelectionEvent e) {
                updateRightTreeModel();
                final StringBuffer denyRules = new StringBuffer();
                final StringBuffer allowRules = new StringBuffer();
                final TreePath[] paths = myLeftTree.getSelectionPaths();
                if (paths == null) {
                  return;
                }
                for (TreePath path : paths) {
                  PackageDependenciesNode selectedNode =
                      (PackageDependenciesNode) path.getLastPathComponent();
                  traverseToLeaves(selectedNode, denyRules, allowRules);
                }
                if (denyRules.length() + allowRules.length() > 0) {
                  StatusBar.Info.set(
                      AnalysisScopeBundle.message(
                          "status.bar.rule.violation.message",
                          ((denyRules.length() == 0 || allowRules.length() == 0) ? 1 : 2),
                          (denyRules.length() > 0
                                  ? denyRules.toString() + (allowRules.length() > 0 ? "; " : "")
                                  : " ")
                              + (allowRules.length() > 0 ? allowRules.toString() : " ")),
                      myProject);
                } else {
                  StatusBar.Info.set(
                      AnalysisScopeBundle.message("status.bar.no.rule.violation.message"),
                      myProject);
                }
              }
            });

    myRightTree
        .getSelectionModel()
        .addTreeSelectionListener(
            new TreeSelectionListener() {
              public void valueChanged(TreeSelectionEvent e) {
                SwingUtilities.invokeLater(
                    new Runnable() {
                      public void run() {
                        final Set<PsiFile> searchIn = getSelectedScope(myLeftTree);
                        final Set<PsiFile> searchFor = getSelectedScope(myRightTree);
                        if (searchIn.isEmpty() || searchFor.isEmpty()) {
                          myUsagesPanel.setToInitialPosition();
                          processDependencies(
                              searchIn,
                              searchFor,
                              new Processor<List<PsiFile>>() { // todo do not show too many usages
                                public boolean process(final List<PsiFile> path) {
                                  searchFor.add(path.get(1));
                                  return true;
                                }
                              });
                        } else {
                          myUsagesPanel.findUsages(searchIn, searchFor);
                        }
                      }
                    });
              }
            });

    initTree(myLeftTree, false);
    initTree(myRightTree, true);

    if (builders.size() == 1) {
      AnalysisScope scope = builders.get(0).getScope();
      if (scope.getScopeType() == AnalysisScope.FILE) {
        Set<PsiFile> oneFileSet = myDependencies.keySet();
        if (oneFileSet.size() == 1) {
          selectElementInLeftTree(oneFileSet.iterator().next());
          return;
        }
      }
    }
    TreeUtil.selectFirstNode(myLeftTree);
  }
  @Override
  protected boolean setupConfigurationFromContext(
      SubstepsRunConfiguration substepsRunConfiguration,
      ConfigurationContext configurationContext,
      Ref<PsiElement> ref) {

    if (ref != null) {

      log.debug("ref: " + ref.toString());

      // TODO in the project view, a feature is IFileElementType: FeatureElementTypes.FEATURE_FILE
      // structure view on a scenario, the ref: ScenarioImpl(SCENARIO_BLOCK_ELEMENT_TYPE)
      PsiElement psiElement = ref.get();
      if (psiElement != null) {

        String scenarioName = null;
        boolean buildRunConfig = false;

        if (psiElement instanceof ScenarioImpl) {
          ScenarioImpl scenarioImpl = (ScenarioImpl) psiElement;
          buildRunConfig = true;
          scenarioName = scenarioImpl.getScenarioName();
        } else if (psiElement instanceof FeatureFileImpl) {
          buildRunConfig = true;
        }

        if (buildRunConfig) {
          SubstepsRunnerConfigurationModel model = new SubstepsRunnerConfigurationModel();

          model.setScenarioName(scenarioName);

          String featureFilePath = configurationContext.getLocation().getVirtualFile().getPath();
          model.setPathToFeature(featureFilePath);

          String featureName = configurationContext.getLocation().getVirtualFile().getName();

          Module module = configurationContext.getModule();

          final Set<String> stepImplClassNames =
              SubstepLibraryManager.INSTANCE.getStepImplClassNamesFromProjectLibraries(module);

          final Set<String> substepDefDirectory = new HashSet<>();

          AnalysisScope moduleScope = new AnalysisScope(module);
          moduleScope.accept(
              new PsiRecursiveElementVisitor() {
                @Override
                public void visitFile(final PsiFile file) {

                  if (file instanceof PsiJavaFile) {

                    PsiJavaFile psiJavaFile = (PsiJavaFile) file;
                    final PsiClass[] psiClasses = psiJavaFile.getClasses();

                    // final PsiClass psiClass =
                    // JavaPsiFacade.getInstance(thisProject).findClass(fqn,
                    // psiJavaFile.getResolveScope());

                    for (PsiClass psiClass : psiClasses) {

                      if (SubstepsCompletionContributor.isStepImplementationsClass(psiClass)) {

                        stepImplClassNames.add(psiClass.getQualifiedName());
                      }
                    }
                  } else if (file instanceof SubstepsDefinitionFile) {

                    String parentPath = file.getParent().getVirtualFile().getPath();

                    if (substepDefDirectory.isEmpty()) {
                      substepDefDirectory.add(parentPath);
                    } else if (!substepDefDirectory.contains(parentPath)) {
                      // find the common ancestor between what's already in and this parent
                      String current = substepDefDirectory.iterator().next();

                      int commonLength = StringUtils.indexOfDifference(current, parentPath);
                      substepDefDirectory.remove(current);

                      String common = current.substring(0, commonLength);

                      log.debug(
                          "current path for substeps: "
                              + current
                              + " got this time: "
                              + parentPath
                              + " common: "
                              + common);

                      substepDefDirectory.add(common);
                    }
                  }
                }
              });

          model.setWorkingDir(module.getModuleFile().getParent().getCanonicalPath());

          model.setStepImplentationClassNames(
              stepImplClassNames.toArray(new String[stepImplClassNames.size()]));

          model.setSubStepDefinitionDirectory(substepDefDirectory.iterator().next());

          try {
            model
                .getJavaParameters()
                .configureByModule(module, JavaParameters.JDK_AND_CLASSES_AND_TESTS);

            Sdk jdk = model.getJavaParameters().getJdk();

            model.setHomePath(jdk.getHomePath());
            model.setVersionString(jdk.getVersionString());

            log.debug(
                "configuring substeps runtime with classpath:\n"
                    + model.getJavaParameters().getClassPath().getPathsString());

            model.setClassPathString(model.getJavaParameters().getClassPath().getPathsString());

          } catch (CantRunException e) {
            log.error("can't run", e);
            return false;
          }

          //                model.setModule(configurationContext.getModule());

          substepsRunConfiguration.setModel(model);

          if (scenarioName != null) {
            substepsRunConfiguration.setName(featureName + ":" + scenarioName);
          } else {
            substepsRunConfiguration.setName(featureName);
          }

          //                this results in two run configs!
          RunManager runManager = RunManager.getInstance(configurationContext.getProject());
          //                RunnerAndConfigurationSettings runAndConfigSettings =
          // runManager.createConfiguration(substepsRunConfiguration,
          // this.getConfigurationFactory());
          //                runManager.addConfiguration(runAndConfigSettings, false);

          List<RunConfiguration> configurationsList = runManager.getConfigurationsList(configType);

          for (RunConfiguration runConfig : configurationsList) {
            SubstepsRunConfiguration substepsRunConfig = (SubstepsRunConfiguration) runConfig;

            log.debug("got substeps run config: " + substepsRunConfig.getName());
          }
          return true;
        }
      }

      //            if (psiElement != null && psiElement.getContainingFile() != null) {
      //
      //                if (psiElement.getContainingFile() instanceof FeatureFile) {
      //
      //                    SubstepsRunnerConfigurationModel model = new
      // SubstepsRunnerConfigurationModel();
      //
      //                    String featureFilePath =
      // configurationContext.getLocation().getVirtualFile().getPath();
      //                    model.setPathToFeature(featureFilePath);
      //
      //                    String featureName =
      // configurationContext.getLocation().getVirtualFile().getName();
      //
      //                    Module module = configurationContext.getModule();
      //
      //                    final Set<String> stepImplClassNames =
      // SubstepLibraryManager.INSTANCE.getStepImplClassNamesFromProjectLibraries(module);
      //
      //                    final Set<String> substepDefDirectory = new HashSet<>();
      //
      //                    AnalysisScope moduleScope = new AnalysisScope(module);
      //                    moduleScope.accept(new PsiRecursiveElementVisitor() {
      //                        @Override
      //                        public void visitFile(final PsiFile file) {
      //
      //                        if (file instanceof PsiJavaFile) {
      //
      //                            PsiJavaFile psiJavaFile = (PsiJavaFile) file;
      //                            final PsiClass[] psiClasses = psiJavaFile.getClasses();
      //
      //                            //final PsiClass psiClass =
      // JavaPsiFacade.getInstance(thisProject).findClass(fqn, psiJavaFile.getResolveScope());
      //
      //                            for (PsiClass psiClass : psiClasses) {
      //
      //                                if
      // (SubstepsCompletionContributor.isStepImplementationsClass(psiClass)) {
      //
      //                                    stepImplClassNames.add(psiClass.getQualifiedName());
      //                                }
      //                            }
      //                        } else if (file instanceof SubstepsDefinitionFile) {
      //
      //                            String parentPath = file.getParent().getVirtualFile().getPath();
      //
      //                            if (substepDefDirectory.isEmpty()) {
      //                                substepDefDirectory.add(parentPath);
      //                            } else if (!substepDefDirectory.contains(parentPath)) {
      //                                // find the common ancestor between what's already in and
      // this parent
      //                                String current = substepDefDirectory.iterator().next();
      //
      //                                int commonLength = StringUtils.indexOfDifference(current,
      // parentPath);
      //                                substepDefDirectory.remove(current);
      //
      //                                String common = current.substring(0, commonLength);
      //
      //                                log.debug("current path for substeps: " + current + " got
      // this time: " + parentPath + " common: " + common);
      //
      //                                substepDefDirectory.add(common);
      //
      //                            }
      //                        }
      //                        }
      //                    });
      //
      //
      //
      // model.setWorkingDir(module.getModuleFile().getParent().getCanonicalPath());
      //
      //
      //                    model.setStepImplentationClassNames(stepImplClassNames.toArray(new
      // String[stepImplClassNames.size()]));
      //
      //
      // model.setSubStepDefinitionDirectory(substepDefDirectory.iterator().next());
      //
      //                    try {
      //                        model.getJavaParameters().configureByModule(module,
      // JavaParameters.JDK_AND_CLASSES_AND_TESTS);
      //
      //                        Sdk jdk = model.getJavaParameters().getJdk();
      //
      //                        model.setHomePath(jdk.getHomePath());
      //                        model.setVersionString(jdk.getVersionString());
      //
      //                        log.debug("configuring substeps runtime with classpath:\n" +
      // model.getJavaParameters().getClassPath().getPathsString());
      //
      //
      // model.setClassPathString(model.getJavaParameters().getClassPath().getPathsString());
      //
      //                    } catch (CantRunException e) {
      //                        log.error("can't run", e);
      //                        return false;
      //                    }
      //
      ////                model.setModule(configurationContext.getModule());
      //
      //                    substepsRunConfiguration.setModel(model);
      //
      //                    substepsRunConfiguration.setName(featureName);
      //
      ////                this results in two run configs!
      //                    RunManager runManager =
      // RunManager.getInstance(configurationContext.getProject());
      ////                RunnerAndConfigurationSettings runAndConfigSettings =
      // runManager.createConfiguration(substepsRunConfiguration, this.getConfigurationFactory());
      ////                runManager.addConfiguration(runAndConfigSettings, false);
      //
      //                    List<RunConfiguration> configurationsList =
      // runManager.getConfigurationsList(configType);
      //
      //                    for (RunConfiguration runConfig : configurationsList) {
      //                        SubstepsRunConfiguration substepsRunConfig =
      // (SubstepsRunConfiguration) runConfig;
      //
      //                        log.debug("got substeps run config: " +
      //                                substepsRunConfig.getName());
      //
      //                    }
      //                    return true;
      //                }
      //            }
    }
    return false;
  }