public void replacePrefix(final String presentPrefix, final String newPrefix) {
    if (!performGuardedChange(
        new Runnable() {
          @Override
          public void run() {
            EditorModificationUtil.deleteSelectedText(myEditor);
            int offset = myEditor.getCaretModel().getOffset();
            final int start = offset - presentPrefix.length();
            myEditor.getDocument().replaceString(start, offset, newPrefix);

            Map<LookupElement, PrefixMatcher> newMatchers =
                new HashMap<LookupElement, PrefixMatcher>();
            for (LookupElement item : getItems()) {
              if (item.isValid()) {
                PrefixMatcher matcher = itemMatcher(item).cloneWithPrefix(newPrefix);
                if (matcher.prefixMatches(item)) {
                  newMatchers.put(item, matcher);
                }
              }
            }
            myMatchers.clear();
            myMatchers.putAll(newMatchers);

            myOffsets.clearAdditionalPrefix();

            myEditor.getCaretModel().moveToOffset(start + newPrefix.length());
          }
        })) {
      return;
    }
    synchronized (myList) {
      myPresentableArranger.prefixChanged(this);
    }
    refreshUi(true, true);
  }
 void appendPrefix(char c) {
   checkValid();
   myOffsets.appendPrefix(c);
   synchronized (myList) {
     myPresentableArranger.prefixChanged(this);
   }
   requestResize();
   refreshUi(false, true);
   ensureSelectionVisible(true);
 }
 private boolean checkReused() {
   synchronized (myList) {
     if (myPresentableArranger != myArranger) {
       myPresentableArranger = myArranger;
       myOffsets.clearAdditionalPrefix();
       myPresentableArranger.prefixChanged(this);
       return true;
     }
     return false;
   }
 }
  public boolean addItem(LookupElement item, PrefixMatcher matcher) {
    LookupElementPresentation presentation = renderItemApproximately(item);
    if (containsDummyIdentifier(presentation.getItemText())
        || containsDummyIdentifier(presentation.getTailText())
        || containsDummyIdentifier(presentation.getTypeText())) {
      return false;
    }

    myMatchers.put(item, matcher);
    updateLookupWidth(item, presentation);
    synchronized (myList) {
      myArranger.addElement(this, item, presentation);
    }
    return true;
  }
  public void resort(boolean addAgain) {
    final List<LookupElement> items = getItems();

    synchronized (myList) {
      myPresentableArranger.prefixChanged(this);
      getListModel().removeAll();
    }

    if (addAgain) {
      for (final LookupElement item : items) {
        addItem(item, itemMatcher(item));
      }
    }
    refreshUi(true, true);
  }
  private boolean updateList(boolean onExplicitAction, boolean reused) {
    if (!ApplicationManager.getApplication().isUnitTestMode()) {
      ApplicationManager.getApplication().assertIsDispatchThread();
    }
    checkValid();

    CollectionListModel<LookupElement> listModel = getListModel();

    Pair<List<LookupElement>, Integer> pair;
    synchronized (myList) {
      pair = myPresentableArranger.arrangeItems(this, onExplicitAction || reused);
    }

    List<LookupElement> items = pair.first;
    Integer toSelect = pair.second;
    if (toSelect == null || toSelect < 0 || items.size() > 0 && toSelect >= items.size()) {
      LOG.error(
          "Arranger "
              + myPresentableArranger
              + " returned invalid selection index="
              + toSelect
              + "; items="
              + items);
      toSelect = 0;
    }

    myOffsets.checkMinPrefixLengthChanges(items, this);
    List<LookupElement> oldModel = listModel.toList();

    listModel.removeAll();
    if (!items.isEmpty()) {
      listModel.add(items);
    } else {
      addEmptyItem(listModel);
    }

    updateListHeight(listModel);

    myList.setSelectedIndex(toSelect);
    return !ContainerUtil.equalsIdentity(oldModel, items);
  }
  boolean truncatePrefix(boolean preserveSelection) {
    if (!myOffsets.truncatePrefix()) {
      return false;
    }

    if (preserveSelection) {
      markSelectionTouched();
    }

    boolean shouldUpdate;
    synchronized (myList) {
      shouldUpdate = myPresentableArranger == myArranger;
      myPresentableArranger.prefixChanged(this);
    }
    requestResize();
    if (shouldUpdate) {
      refreshUi(false, true);
      ensureSelectionVisible(true);
    }

    return true;
  }
  @Override
  public void addElement(
      Lookup lookup, LookupElement element, LookupElementPresentation presentation) {
    StatisticsWeigher.clearBaseStatisticsInfo(element);

    final String invariant =
        presentation.getItemText()
            + "###"
            + getTailTextOrSpace(presentation)
            + "###"
            + presentation.getTypeText();
    element.putUserData(PRESENTATION_INVARIANT, invariant);

    CompletionSorterImpl sorter = obtainSorter(element);
    Classifier<LookupElement> classifier = myClassifiers.get(sorter);
    if (classifier == null) {
      myClassifiers.put(sorter, classifier = sorter.buildClassifier(new AlphaClassifier(lookup)));
    }
    classifier.addElement(element);

    super.addElement(lookup, element, presentation);
  }
 public Map<LookupElement, StringBuilder> getRelevanceStrings() {
   synchronized (myList) {
     return myPresentableArranger.getRelevanceStrings();
   }
 }
 public void markReused() {
   synchronized (myList) {
     myArranger = myArranger.createEmptyCopy();
   }
   requestResize();
 }
 @Override
 public void prefixChanged(Lookup lookup) {
   myPrefixChanges++;
   myFrozenItems.clear();
   super.prefixChanged(lookup);
 }