private void trimToLimit(ProcessingContext context) {
    List<LookupElement> removed;
    synchronized (this) {
      if (myItems.size() < myLimit) return;

      List<LookupElement> items = getMatchingItems();
      Iterator<LookupElement> iterator = sortByRelevance(groupItemsBySorter(items)).iterator();

      Set<LookupElement> retainedSet = new ReferenceOpenHashSet<>();
      retainedSet.addAll(getPrefixItems(true));
      retainedSet.addAll(getPrefixItems(false));
      retainedSet.addAll(myFrozenItems);
      while (retainedSet.size() < myLimit / 2 && iterator.hasNext()) {
        retainedSet.add(iterator.next());
      }

      if (!iterator.hasNext()) return;

      removed = retainItems(retainedSet);
      if (!myOverflow) {
        myOverflow = true;
        myProcess.addAdvertisement(AnalysisBundle.message("completion.not.all.variants.are.shown"), null);

        // restart completion on any prefix change
        myProcess.addWatchedPrefix(0, StandardPatterns.string());

        if (ApplicationManager.getApplication().isUnitTestMode()) printTestWarning();
      }
    }

    for (LookupElement element : removed) {
      removeItem(element, context);
    }
  }
private void trimToLimit(ProcessingContext context)