boolean processFiles(@NotNull FindSymbolParameters parameters,
                         @NotNull Processor<? super FoundItemDescriptor<?>> processor,
                         @NotNull Ref<Boolean> hasSuggestions,
                         @NotNull DirectoryPathMatcher dirMatcher) {

      List<MatchResult> matchingNames = this.matchingNames;
      if (patternSuffix.length() <= 3 && !dirMatcher.dirPattern.isEmpty()) {
        // just enumerate over files
        // otherwise there are too many names matching the remaining few letters, and querying index for all of them with a very constrained scope is expensive
        Set<String> existingNames = dirMatcher.findFileNamesMatchingIfCheap(patternSuffix.charAt(0), matcher);
        if (existingNames != null) {
          matchingNames = ContainerUtil.filter(matchingNames, mr -> existingNames.contains(mr.elementName));
        }
      }
      MinusculeMatcher qualifierMatcher = getQualifiedNameMatcher(parameters.getLocalPatternName());
      Comparator<MatchResult> byNameWithoutExtension = Comparator.comparing(
        mr -> StringUtil.toLowerCase(FileUtilRt.getNameWithoutExtension(mr.elementName)));
      Comparator<MatchResult> matchingDegreeComparator = matchingDegreeComparator();
      matchingNames = ContainerUtil.sorted(matchingNames, matchingDegreeComparator);
      // comparator1.thenComparing(comparator2) is too slow, let's lazily apply comparator2 as needed below
      Function<List<MatchResult>, List<MatchResult>> sortGroup = new Function<>() {
        final Set<Object> sortedGroups = CollectionFactory.createCustomHashingStrategySet(HashingStrategy.identity());
        @Override
        public List<MatchResult> apply(List<MatchResult> results) {
          if (sortedGroups.add(results)) {
            results.sort(byNameWithoutExtension);
          }
          return results;
        }
      };

      GlobalSearchScope scope = dirMatcher.narrowDown(parameters.getSearchScope());
      FindSymbolParameters parametersAdjusted = parameters.withScope(scope);

      List<List<MatchResult>> groups = group(matchingNames, matchingDegreeComparator);
      Function<String, Object[]> indexResult = key -> myModel.getElementsByName(key, parametersAdjusted, indicator);

      for (List<MatchResult> group : groups) {
        List<List<MatchResult>> sortedNames = group(sortGroup.apply(group), byNameWithoutExtension);
        JBIterable<FoundItemDescriptor<PsiFileSystemItem>> filesMatchingPath = JBIterable.from(sortedNames)
          .flatMap(nameGroup -> getItemsForNames(scope, nameGroup, indexResult));
        Iterable<FoundItemDescriptor<PsiFileSystemItem>> matchedFiles =
          parameters.getLocalPatternName().isEmpty()
          ? filesMatchingPath
          : matchQualifiers(qualifierMatcher, filesMatchingPath, parameters.getCompletePattern());

        matchedFiles = moveDirectoriesToEnd(matchedFiles);
        Processor<FoundItemDescriptor<PsiFileSystemItem>> trackingProcessor = res -> {
          hasSuggestions.set(true);
          return processor.process(res);
        };
        if (!ContainerUtil.process(matchedFiles, trackingProcessor)) {
          return false;
        }
      }

      // let the framework switch to searching outside project to display these well-matching suggestions
      // instead of worse-matching ones in project (that are very expensive to calculate)
      return hasSuggestions.get() ||
             parameters.isSearchInLibraries() ||
             !hasSuggestionsOutsideProject(parameters.getCompletePattern(), matchingNames, dirMatcher);
    }
boolean processFiles(@NotNull FindSymbolParameters parameters,
                         @NotNull Processor<? super FoundItemDescriptor<?>> processor,
                         @NotNull Ref<Boolean> hasSuggestions,
                         @NotNull DirectoryPathMatcher dirMatcher)