@Override
    public void execute(@NotNull final Editor editor, final DataContext dataContext) {
      final LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(editor);
      if (lookup == null) {
        throw new AssertionError(
            "The last lookup disposed at: "
                + LookupImpl.getLastLookupDisposeTrace()
                + "\n-----------------------\n");
      }

      if (finishingChar == Lookup.NORMAL_SELECT_CHAR) {
        if (!lookup.isFocused()) {
          FeatureUsageTracker.getInstance()
              .triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ENTER);
        }
      } else if (finishingChar == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) {
        FeatureUsageTracker.getInstance()
            .triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_FINISH_BY_SMART_ENTER);
      } else if (finishingChar == Lookup.REPLACE_SELECT_CHAR) {
        FeatureUsageTracker.getInstance()
            .triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_REPLACE);
      } else if (finishingChar == '.') {
        FeatureUsageTracker.getInstance()
            .triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_FINISH_BY_CONTROL_DOT);
      }

      lookup.finishLookup(finishingChar);
    }
  public CompletionProgressIndicator(
      final Editor editor,
      CompletionParameters parameters,
      CodeCompletionHandlerBase handler,
      Semaphore freezeSemaphore,
      final OffsetMap offsetMap,
      boolean hasModifiers) {
    myEditor = editor;
    myParameters = parameters;
    myHandler = handler;
    myFreezeSemaphore = freezeSemaphore;
    myOffsetMap = offsetMap;
    myLookup = (LookupImpl) parameters.getLookup();

    myLookup.setArranger(new CompletionLookupArranger(parameters, this));

    myLookup.addLookupListener(myLookupListener);
    myLookup.setCalculating(true);

    myLookupManagerListener =
        new PropertyChangeListener() {
          @Override
          public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() != null) {
              LOG.error(
                  "An attempt to change the lookup during completion, phase = "
                      + CompletionServiceImpl.getCompletionPhase());
            }
          }
        };
    LookupManager.getInstance(getProject()).addPropertyChangeListener(myLookupManagerListener);

    myQueue =
        new MergingUpdateQueue(
            "completion lookup progress", 200, true, myEditor.getContentComponent());
    myQueue.setPassThrough(false);

    ApplicationManager.getApplication().assertIsDispatchThread();
    addMapToDispose(offsetMap);

    if (ApplicationManager.getApplication().isUnitTestMode()) {
      return;
    }

    if (!myLookup.isShown()) {
      scheduleAdvertising();
    }

    if (hasModifiers) {
      trackModifiers();
    }
  }
  public void closeAndFinish(boolean hideLookup) {
    if (!myLookup.isLookupDisposed()) {
      Lookup lookup = LookupManager.getActiveLookup(myEditor);
      LOG.assertTrue(lookup == myLookup, "lookup changed: " + lookup + "; " + this);
    }
    myLookup.removeLookupListener(myLookupListener);
    finishCompletionProcess(true);
    CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());

    if (hideLookup) {
      LookupManager.getInstance(getProject()).hideActiveLookup();
    }
  }
  void duringCompletion(CompletionInitializationContext initContext) {
    if (isAutopopupCompletion()) {
      if (shouldFocusLookup(myParameters)) {
        myLookup.setFocused(true);
      } else if (FeatureUsageTracker.getInstance()
          .isToBeAdvertisedInLookup(
              CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ENTER, getProject())) {
        myLookup.addAdvertisement(
            "Press "
                + CompletionContributor.getActionShortcut(
                    IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_ALWAYS)
                + " to choose the first suggestion");
      }
      if (!myEditor.isOneLineMode()
          && FeatureUsageTracker.getInstance()
              .isToBeAdvertisedInLookup(
                  CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ARROWS, getProject())) {
        myLookup.addAdvertisement(
            CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_DOWN)
                + " and "
                + CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_UP)
                + " will move caret down and up in the editor");
      }
    }

    ProgressManager.checkCanceled();

    if (!initContext
        .getOffsetMap()
        .wasModified(CompletionInitializationContext.IDENTIFIER_END_OFFSET)) {
      try {
        final int selectionEndOffset = initContext.getSelectionEndOffset();
        final PsiReference reference = initContext.getFile().findReferenceAt(selectionEndOffset);
        if (reference != null) {
          initContext.setReplacementOffset(findReplacementOffset(selectionEndOffset, reference));
        }
      } catch (IndexNotReadyException ignored) {
      }
    }

    for (CompletionContributor contributor :
        CompletionContributor.forLanguage(initContext.getPositionLanguage())) {
      ProgressManager.checkCanceled();
      if (DumbService.getInstance(initContext.getProject()).isDumb()
          && !DumbService.isDumbAware(contributor)) {
        continue;
      }

      contributor.duringCompletion(initContext);
    }
  }
  private boolean updateLookup() {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (isOutdated() || !shouldShowLookup()) return false;

    boolean justShown = false;
    if (!myLookup.isShown()) {
      if (hideAutopopupIfMeaningless()) {
        return false;
      }

      if (Registry.is("dump.threads.on.empty.lookup")
          && myLookup.isCalculating()
          && myLookup.getItems().isEmpty()) {
        PerformanceWatcher.getInstance().dumpThreads(true);
      }

      if (StringUtil.isEmpty(myLookup.getAdvertisementText()) && !isAutopopupCompletion()) {
        final String text = DefaultCompletionContributor.getDefaultAdvertisementText(myParameters);
        if (text != null) {
          myLookup.setAdvertisementText(text);
        }
      }

      if (!myLookup.showLookup()) {
        return false;
      }
      justShown = true;
    }
    myLookup.refreshUi(true, justShown);
    hideAutopopupIfMeaningless();
    if (justShown) {
      myLookup.ensureSelectionVisible();
    }
    return true;
  }
  @NotNull
  private LookupImpl obtainLookup(Editor editor) {
    CompletionAssertions.checkEditorValid(editor);
    LookupImpl existing = (LookupImpl) LookupManager.getActiveLookup(editor);
    if (existing != null && existing.isCompletion()) {
      existing.markReused();
      if (!autopopup) {
        existing.setFocusDegree(LookupImpl.FocusDegree.FOCUSED);
      }
      return existing;
    }

    LookupImpl lookup =
        (LookupImpl)
            LookupManager.getInstance(editor.getProject())
                .createLookup(
                    editor, LookupElement.EMPTY_ARRAY, "", new LookupArranger.DefaultArranger());
    if (editor.isOneLineMode()) {
      lookup.setCancelOnClickOutside(true);
      lookup.setCancelOnOtherWindowOpen(true);
    }
    lookup.setFocusDegree(
        autopopup ? LookupImpl.FocusDegree.UNFOCUSED : LookupImpl.FocusDegree.FOCUSED);
    return lookup;
  }
  public void perform(final boolean generatePrivate) {
    final Runnable runnable =
        new Runnable() {
          public void run() {
            final DocumentEx document = (DocumentEx) myEditor.getDocument();

            int exprOffset = myExprMarker.getStartOffset();
            final int lineOffset = getLineOffset(document, exprOffset);
            if (generatePrivate) {
              final Collection<RangeMarker> leftGreedyMarker = ContainerUtil.newArrayList();
              final Collection<RangeMarker> emptyMarkers = ContainerUtil.newArrayList();
              for (RangeHighlighter rangeHighlighter :
                  myEditor.getMarkupModel().getAllHighlighters()) {
                collectRangeMarker(rangeHighlighter, lineOffset, leftGreedyMarker, emptyMarkers);
              }
              document.processRangeMarkers(
                  new Processor<RangeMarker>() {
                    @Override
                    public boolean process(RangeMarker rangeMarker) {
                      collectRangeMarker(rangeMarker, lineOffset, leftGreedyMarker, emptyMarkers);
                      return true;
                    }
                  });
              setLeftGreedy(leftGreedyMarker, false);
              setRightGreedy(emptyMarkers, true);

              // workaround for shifting empty ranges to the left
              document.insertString(lineOffset, " ");
              document.insertString(lineOffset, PRIVATE);
              document.deleteString(
                  lineOffset + PRIVATE.length(), lineOffset + PRIVATE.length() + 1);

              setLeftGreedy(leftGreedyMarker, true);
              setRightGreedy(emptyMarkers, false);
            } else {
              int idx = document.getText().indexOf(PRIVATE, lineOffset);
              if (idx > -1 && idx < exprOffset) {
                document.deleteString(idx, idx + PRIVATE.length());
              }
            }
            PsiDocumentManager.getInstance(myProject).commitDocument(document);
          }
        };
    final LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(myEditor);
    if (lookup != null) {
      lookup.performGuardedChange(runnable);
    } else {
      runnable.run();
    }
  }
  public synchronized void addItem(final CompletionResult item) {
    if (!isRunning()) return;
    ProgressManager.checkCanceled();

    final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
    if (!unitTestMode) {
      LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
    }

    LOG.assertTrue(myParameters.getPosition().isValid());

    myItemSorters.put(item.getLookupElement(), (CompletionSorterImpl) item.getSorter());
    myLookup.addItem(item.getLookupElement(), item.getPrefixMatcher());
    myCount++;

    if (myCount == 1) {
      ApplicationManager.getApplication()
          .executeOnPooledThread(
              new Runnable() {
                public void run() {
                  try {
                    Thread.sleep(300);
                  } catch (InterruptedException ignore) {
                  } finally {
                    myFreezeSemaphore.up();
                  }
                }
              });
    }
    myQueue.queue(myUpdate);
  }
  public synchronized void addItem(final CompletionResult item) {
    if (!isRunning()) return;
    ProgressManager.checkCanceled();

    final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
    if (!unitTestMode) {
      LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
    }

    LOG.assertTrue(myParameters.getPosition().isValid());

    myItemSorters.put(item.getLookupElement(), (CompletionSorterImpl) item.getSorter());
    myLookup.addItem(item.getLookupElement(), item.getPrefixMatcher());
    myCount++;

    if (myCount == 1) {
      new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this)
          .addRequest(
              new Runnable() {
                @Override
                public void run() {
                  myFreezeSemaphore.up();
                }
              },
              300);
    }
    myQueue.queue(myUpdate);
  }
  private boolean hideAutopopupIfMeaningless() {
    if (!myLookup.isLookupDisposed()
        && isAutopopupCompletion()
        && !myLookup.isSelectionTouched()
        && !myLookup.isCalculating()) {
      myLookup.refreshUi(true);
      final List<LookupElement> items = myLookup.getItems();

      for (LookupElement item : items) {
        if (!myLookup.itemPattern(item).equals(item.getLookupString())) {
          return false;
        }

        if (item.isValid()) {
          final LookupElementPresentation presentation = new LookupElementPresentation();
          item.renderElement(presentation);
          if (StringUtil.isNotEmpty(presentation.getTailText())) {
            return false;
          }
        }
      }

      myLookup.hideLookup(false);
      LOG.assertTrue(CompletionServiceImpl.getCompletionService().getCurrentCompletion() == null);
      CompletionServiceImpl.setCompletionPhase(new CompletionPhase.EmptyAutoPopup(this));
      return true;
    }
    return false;
  }
  public void scheduleRestart() {
    ApplicationManager.getApplication().assertIsDispatchThread();
    cancel();

    final CompletionProgressIndicator current =
        CompletionServiceImpl.getCompletionService().getCurrentCompletion();
    if (this != current) {
      LOG.error(current + "!=" + this);
    }

    if (isAutopopupCompletion() && !myLookup.isShown()) {
      if (CompletionServiceImpl.getCompletionService().getCurrentCompletion() == this) {
        closeAndFinish(true);
      }

      AutoPopupController.getInstance(getProject()).scheduleAutoPopup(myEditor, null);
      return;
    }

    hideAutopopupIfMeaningless();

    CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
    if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
      ((CompletionPhase.CommittingDocuments) oldPhase).replaced = true;
    }

    final CompletionPhase.CommittingDocuments phase =
        new CompletionPhase.CommittingDocuments(this, myEditor);
    CompletionServiceImpl.setCompletionPhase(phase);

    final Project project = getProject();
    ApplicationManager.getApplication()
        .invokeLater(
            new Runnable() {
              @Override
              public void run() {
                CompletionAutoPopupHandler.runLaterWithCommitted(
                    project,
                    myEditor.getDocument(),
                    new Runnable() {
                      @Override
                      public void run() {
                        if (phase.checkExpired()) return;

                        CompletionAutoPopupHandler.invokeCompletion(
                            myParameters.getCompletionType(),
                            isAutopopupCompletion(),
                            project,
                            myEditor,
                            myParameters.getInvocationCount(),
                            true);
                      }
                    });
              }
            },
            project.getDisposed());
  }
  private static int getItemToSelect(
      LookupImpl lookup,
      List<LookupElement> items,
      boolean onExplicitAction,
      @Nullable LookupElement mostRelevant) {
    if (items.isEmpty() || lookup.getFocusDegree() == LookupImpl.FocusDegree.UNFOCUSED) {
      return 0;
    }

    if (lookup.isSelectionTouched() || !onExplicitAction) {
      final LookupElement lastSelection = lookup.getCurrentItem();
      int old = ContainerUtil.indexOfIdentity(items, lastSelection);
      if (old >= 0) {
        return old;
      }

      Object selectedValue = ((LookupImpl) lookup).getList().getSelectedValue();
      if (selectedValue instanceof EmptyLookupItem
          && ((EmptyLookupItem) selectedValue).isLoading()) {
        int index = ((LookupImpl) lookup).getList().getSelectedIndex();
        if (index >= 0 && index < items.size()) {
          return index;
        }
      }

      for (int i = 0; i < items.size(); i++) {
        String invariant = PRESENTATION_INVARIANT.get(items.get(i));
        if (invariant != null && invariant.equals(PRESENTATION_INVARIANT.get(lastSelection))) {
          return i;
        }
      }
    }

    String selectedText = lookup.getEditor().getSelectionModel().getSelectedText();
    for (int i = 0; i < items.size(); i++) {
      LookupElement item = items.get(i);
      if (isPrefixItem(lookup, item, true) && !isLiveTemplate(item)
          || item.getLookupString().equals(selectedText)) {
        return i;
      }
    }

    return Math.max(0, ContainerUtil.indexOfIdentity(items, mostRelevant));
  }
  public boolean isRepeatedInvocation(CompletionType completionType, Editor editor) {
    if (completionType != myParameters.getCompletionType() || editor != myEditor) {
      return false;
    }

    if (isAutopopupCompletion() && !myLookup.mayBeNoticed()) {
      return false;
    }

    return true;
  }
    @Override
    public boolean isEnabled(Editor editor, DataContext dataContext) {
      LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(editor);
      if (lookup == null) return false;
      if (!lookup.isAvailableToUser()) return false;
      if (focusedOnly && !CompletionPreview.hasPreview(lookup) && !lookup.isFocused()) return false;
      if (finishingChar == Lookup.NORMAL_SELECT_CHAR
              && hasTemplatePrefix(lookup, TemplateSettings.ENTER_CHAR)
          || finishingChar == Lookup.REPLACE_SELECT_CHAR
              && hasTemplatePrefix(lookup, TemplateSettings.TAB_CHAR)) {
        return false;
      }
      if (finishingChar == Lookup.REPLACE_SELECT_CHAR) {
        if (lookup.isFocused()) {
          return true;
        }
        return !lookup.getItems().isEmpty();
      }

      return true;
    }
  private boolean updateLookup() {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (isOutdated()) return false;

    boolean justShown = false;
    if (!myLookup.isShown() && shouldShowLookup()) {
      if (hideAutopopupIfMeaningless()) {
        return false;
      }

      if (StringUtil.isEmpty(myLookup.getAdvertisementText()) && !isAutopopupCompletion()) {
        final String text = DefaultCompletionContributor.getDefaultAdvertisementText(myParameters);
        if (text != null) {
          myLookup.setAdvertisementText(text);
        }
      }

      if (!myLookup.showLookup()) {
        return false;
      }
      justShown = true;
    }
    myLookup.refreshUi(true);
    hideAutopopupIfMeaningless();
    if (justShown) {
      myLookup.ensureSelectionVisible();
    }
    return true;
  }
        public void itemSelected(LookupEvent event) {
          finishCompletionProcess(false);

          LookupElement item = event.getItem();
          if (item == null) return;

          setMergeCommand();

          CodeCompletionHandlerBase.lookupItemSelected(
              CompletionProgressIndicator.this,
              item,
              event.getCompletionChar(),
              myLookup.getItems());
        }
 private static void ensureEverythingVisibleAdded(
     LookupImpl lookup,
     final LinkedHashSet<LookupElement> model,
     Iterator<LookupElement> byRelevance) {
   JList list = lookup.getList();
   final boolean testMode = ApplicationManager.getApplication().isUnitTestMode();
   final int limit =
       Math.max(list.getLastVisibleIndex(), model.size())
           + ourUISettings.MAX_LOOKUP_LIST_HEIGHT * 3;
   addSomeItems(
       model,
       byRelevance,
       new Condition<LookupElement>() {
         @Override
         public boolean value(LookupElement lastAdded) {
           return !testMode && model.size() >= limit;
         }
       });
 }
  private List<LookupElement> fillModelByRelevance(
      LookupImpl lookup,
      List<LookupElement> items,
      MultiMap<CompletionSorterImpl, LookupElement> inputBySorter,
      @Nullable LookupElement relevantSelection) {
    Iterator<LookupElement> byRelevance = sortByRelevance(inputBySorter).iterator();

    final LinkedHashSet<LookupElement> model = new LinkedHashSet<LookupElement>();

    addPrefixItems(model);
    addFrozenItems(items, model);
    addSomeItems(
        model,
        byRelevance,
        new Condition<LookupElement>() {
          @Override
          public boolean value(LookupElement lastAdded) {
            return model.size() >= MAX_PREFERRED_COUNT;
          }
        });
    addCurrentlySelectedItemToTop(lookup, items, model);

    freezeTopItems(lookup, model);

    ensureItemAdded(items, model, byRelevance, lookup.getCurrentItem());
    ensureItemAdded(items, model, byRelevance, relevantSelection);
    ensureEverythingVisibleAdded(lookup, model, byRelevance);

    ArrayList<LookupElement> result = new ArrayList<LookupElement>(model);
    if (result.size() > 1) {
      LookupElement first = result.get(0);
      if (isLiveTemplate(first) && isPrefixItem(lookup, first, true)) {
        ContainerUtil.swapElements(result, 0, 1);
      }
    }

    return result;
  }
  void scheduleAdvertising() {
    if (myLookup.isAvailableToUser()) {
      return;
    }
    final List<CompletionContributor> list = CompletionContributor.forParameters(myParameters);
    for (final CompletionContributor contributor : list) {
      if (myLookup.getAdvertisementText() != null) return;
      if (!myLookup.isCalculating() && !myLookup.isVisible()) return;

      @SuppressWarnings("deprecation")
      String s = contributor.advertise(myParameters);
      if (myLookup.getAdvertisementText() != null) return;

      if (s != null) {
        myLookup.setAdvertisementText(s);
        ApplicationManager.getApplication()
            .invokeLater(
                new Runnable() {
                  @Override
                  public void run() {
                    if (isAutopopupCompletion() && !myLookup.isAvailableToUser()) {
                      return;
                    }
                    if (!CompletionServiceImpl.isPhase(
                        CompletionPhase.BgCalculation.class,
                        CompletionPhase.ItemsCalculated.class)) {
                      return;
                    }
                    if (CompletionServiceImpl.getCompletionPhase().indicator
                        != CompletionProgressIndicator.this) {
                      return;
                    }

                    updateLookup();
                  }
                },
                myQueue.getModalityState());
        return;
      }
    }
  }
  public static boolean hasTemplatePrefix(LookupImpl lookup, char shortcutChar) {
    lookup.refreshUi(false, false); // to bring the list model up to date

    CompletionProcess completion = CompletionService.getCompletionService().getCurrentCompletion();
    if (completion == null || !completion.isAutopopupCompletion()) {
      return false;
    }

    if (lookup.isSelectionTouched()) {
      return false;
    }

    final PsiFile file = lookup.getPsiFile();
    if (file == null) return false;

    final Editor editor = lookup.getEditor();
    PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument());

    final int end = editor.getCaretModel().getOffset();
    final int start = lookup.getLookupStart();
    final String prefix =
        !lookup.getItems().isEmpty()
            ? editor.getDocument().getText(TextRange.create(start, end))
            : ListTemplatesHandler.getPrefix(editor.getDocument(), end);

    if (TemplateSettings.getInstance().getTemplates(prefix).isEmpty()) {
      return false;
    }

    for (TemplateImpl template :
        SurroundWithTemplateHandler.getApplicableTemplates(editor, file, false)) {
      if (prefix.equals(template.getKey())
          && shortcutChar == TemplateSettings.getInstance().getShortcutChar(template)) {
        return true;
      }
    }
    return false;
  }
 private void freezeTopItems(LookupImpl lookup, LinkedHashSet<LookupElement> model) {
   myFrozenItems.clear();
   if (lookup.isShown()) {
     myFrozenItems.addAll(model);
   }
 }
 private boolean shouldShowLookup() {
   if (isAutopopupCompletion() && myLookup.isCalculating()) {
     return false;
   }
   return true;
 }