@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;
  }
  private static void restoreBlockSelection(
      Editor editor, List<RangeMarker> caretsAfter, int caretLine) {
    int column = -1;
    int minLine = Integer.MAX_VALUE;
    int maxLine = -1;
    for (RangeMarker marker : caretsAfter) {
      if (marker.isValid()) {
        LogicalPosition lp = editor.offsetToLogicalPosition(marker.getStartOffset());
        if (column == -1) {
          column = lp.column;
        } else if (column != lp.column) {
          return;
        }
        minLine = Math.min(minLine, lp.line);
        maxLine = Math.max(maxLine, lp.line);

        if (lp.line == caretLine) {
          editor.getCaretModel().moveToLogicalPosition(lp);
        }
      }
    }
    editor
        .getSelectionModel()
        .setBlockSelection(
            new LogicalPosition(minLine, column), new LogicalPosition(maxLine, column));
  }
  private CompletionInitializationContext runContributorsBeforeCompletion(
      Editor editor, PsiFile psiFile, int invocationCount, Caret caret) {
    final Ref<CompletionContributor> current = Ref.create(null);
    CompletionInitializationContext context =
        new CompletionInitializationContext(
            editor, caret, psiFile, myCompletionType, invocationCount) {
          CompletionContributor dummyIdentifierChanger;

          @Override
          public void setDummyIdentifier(@NotNull String dummyIdentifier) {
            super.setDummyIdentifier(dummyIdentifier);

            if (dummyIdentifierChanger != null) {
              LOG.error(
                  "Changing the dummy identifier twice, already changed by "
                      + dummyIdentifierChanger);
            }
            dummyIdentifierChanger = current.get();
          }
        };
    List<CompletionContributor> contributors =
        CompletionContributor.forLanguage(context.getPositionLanguage());
    Project project = psiFile.getProject();
    List<CompletionContributor> filteredContributors =
        DumbService.getInstance(project).filterByDumbAwareness(contributors);
    for (final CompletionContributor contributor : filteredContributors) {
      current.set(contributor);
      contributor.beforeCompletion(context);
      CompletionAssertions.checkEditorValid(editor);
      assert !PsiDocumentManager.getInstance(project).isUncommited(editor.getDocument())
          : "Contributor " + contributor + " left the document uncommitted";
    }
    return context;
  }
 private static Caret getNextCaretToProcess(@NotNull Editor editor) {
   for (Caret caret : editor.getCaretModel().getAllCarets()) {
     if (caret.getUserData(CARET_PROCESSED) == null) {
       return caret;
     }
   }
   return null;
 }
 public final void invokeCompletion(
     @NotNull final Project project,
     @NotNull final Editor editor,
     int time,
     boolean hasModifiers,
     boolean restarted) {
   clearCaretMarkers(editor);
   invokeCompletion(
       project, editor, time, hasModifiers, restarted, editor.getCaretModel().getPrimaryCaret());
 }
 private static void addCompletionChar(
     Project project,
     CompletionAssertions.WatchingInsertionContext context,
     LookupElement item,
     Editor editor,
     CompletionProgressIndicator indicator,
     char completionChar) {
   int tailOffset = context.getTailOffset();
   if (tailOffset < 0) {
     LOG.info(
         "tailOffset<0 after inserting "
             + item
             + " of "
             + item.getClass()
             + "; invalidated at: "
             + context.invalidateTrace
             + "\n--------");
   } else {
     editor.getCaretModel().moveToOffset(tailOffset);
   }
   if (context.getCompletionChar() == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) {
     final Language language = PsiUtilBase.getLanguageInEditor(editor, project);
     if (language != null) {
       for (SmartEnterProcessor processor : SmartEnterProcessors.INSTANCE.forKey(language)) {
         if (processor.processAfterCompletion(editor, indicator.getParameters().getOriginalFile()))
           break;
       }
     }
   } else if (!editor
       .getCaretModel()
       .supportsMultipleCarets()) { // this will be done outside of runForEach caret context
     DataContext dataContext =
         DataManager.getInstance().getDataContext(editor.getContentComponent());
     EditorActionManager.getInstance()
         .getTypedAction()
         .getHandler()
         .execute(editor, completionChar, dataContext);
   }
 }
  private void insertLookupString(LookupElement item, final int prefix) {
    final String lookupString = getCaseCorrectedLookupString(item);

    final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(myEditor);
    hostEditor
        .getCaretModel()
        .runForEachCaret(
            new CaretAction() {
              @Override
              public void perform(Caret caret) {
                EditorModificationUtil.deleteSelectedText(hostEditor);
                final int caretOffset = hostEditor.getCaretModel().getOffset();
                int lookupStart = Math.max(caretOffset - prefix, 0);

                int len = hostEditor.getDocument().getTextLength();
                LOG.assertTrue(
                    lookupStart >= 0 && lookupStart <= len,
                    "ls: "
                        + lookupStart
                        + " caret: "
                        + caretOffset
                        + " prefix:"
                        + prefix
                        + " doc: "
                        + len);
                LOG.assertTrue(
                    caretOffset >= 0 && caretOffset <= len, "co: " + caretOffset + " doc: " + len);

                hostEditor.getDocument().replaceString(lookupStart, caretOffset, lookupString);

                int offset = lookupStart + lookupString.length();
                hostEditor.getCaretModel().moveToOffset(offset);
                hostEditor.getSelectionModel().removeSelection();
              }
            });

    myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
  }
  public boolean performGuardedChange(Runnable change) {
    checkValid();
    assert !myChangeGuard : "already in change";

    myEditor.getDocument().startGuardedBlockChecking();
    myChangeGuard = true;
    boolean result;
    try {
      result = myOffsets.performGuardedChange(change);
    } finally {
      myEditor.getDocument().stopGuardedBlockChecking();
      myChangeGuard = false;
    }
    if (!result || myDisposed) {
      hide();
      return false;
    }
    if (isVisible()) {
      HintManagerImpl.updateLocation(this, myEditor, myUi.calculatePosition().getLocation());
    }
    checkValid();
    return true;
  }
 private static OffsetMap translateOffsetMapToHost(
     CompletionInitializationContext initContext,
     PsiFile context,
     PsiFile hostFile,
     Editor hostEditor) {
   final InjectedLanguageManager injectedLanguageManager =
       InjectedLanguageManager.getInstance(hostFile.getProject());
   final OffsetMap hostMap = new OffsetMap(hostEditor.getDocument());
   final OffsetMap original = initContext.getOffsetMap();
   for (final OffsetKey key : original.getAllOffsets()) {
     hostMap.addOffset(
         key, injectedLanguageManager.injectedToHost(context, original.getOffset(key)));
   }
   return hostMap;
 }
        @Override
        protected void processKeyEvent(@NotNull final KeyEvent e) {
          final char keyChar = e.getKeyChar();
          if (keyChar == KeyEvent.VK_ENTER || keyChar == KeyEvent.VK_TAB) {
            IdeFocusManager.getInstance(myProject)
                .requestFocus(myEditor.getContentComponent(), true)
                .doWhenDone(
                    new Runnable() {
                      @Override
                      public void run() {
                        IdeEventQueue.getInstance().getKeyEventDispatcher().dispatchKeyEvent(e);
                      }
                    });
            return;
          }

          super.processKeyEvent(e);
        }
  public boolean showLookup() {
    ApplicationManager.getApplication().assertIsDispatchThread();
    checkValid();
    LOG.assertTrue(!myShown);
    myShown = true;
    myStampShown = System.currentTimeMillis();

    if (ApplicationManager.getApplication().isUnitTestMode()) return true;

    if (!myEditor.getContentComponent().isShowing()) {
      hide();
      return false;
    }

    myAdComponent.showRandomText();

    myUi = new LookupUi(this, myAdComponent, myList, myProject);
    myUi.setCalculating(myCalculating);
    Point p = myUi.calculatePosition().getLocation();
    try {
      HintManagerImpl.getInstanceImpl()
          .showEditorHint(
              this,
              myEditor,
              p,
              HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING,
              0,
              false,
              HintManagerImpl.createHintHint(myEditor, p, this, HintManager.UNDER)
                  .setAwtTooltip(false));
    } catch (Exception e) {
      LOG.error(e);
    }

    if (!isVisible() || !myList.isShowing()) {
      hide();
      return false;
    }

    DaemonCodeAnalyzer.getInstance(myProject).disableUpdateByTimer(this);

    return true;
  }
  private static boolean shouldSkipAutoPopup(Editor editor, PsiFile psiFile) {
    int offset = editor.getCaretModel().getOffset();
    int psiOffset = Math.max(0, offset - 1);

    PsiElement elementAt = InjectedLanguageUtil.findInjectedElementNoCommit(psiFile, psiOffset);
    if (elementAt == null) {
      elementAt = psiFile.findElementAt(psiOffset);
    }
    if (elementAt == null) return true;

    Language language = PsiUtilCore.findLanguageFromElement(elementAt);

    for (CompletionConfidence confidence : CompletionConfidenceEP.forLanguage(language)) {
      final ThreeState result = confidence.shouldSkipAutopopup(elementAt, psiFile, offset);
      if (result != ThreeState.UNSURE) {
        LOG.debug(confidence + " has returned shouldSkipAutopopup=" + result);
        return result == ThreeState.YES;
      }
    }
    return false;
  }
  private static Runnable rememberDocumentState(final Editor _editor) {
    final Editor editor = InjectedLanguageUtil.getTopLevelEditor(_editor);
    final String documentText = editor.getDocument().getText();
    final int caret = editor.getCaretModel().getOffset();
    final int selStart = editor.getSelectionModel().getSelectionStart();
    final int selEnd = editor.getSelectionModel().getSelectionEnd();

    final int vOffset = editor.getScrollingModel().getVerticalScrollOffset();
    final int hOffset = editor.getScrollingModel().getHorizontalScrollOffset();

    return new Runnable() {
      @Override
      public void run() {
        DocumentEx document = (DocumentEx) editor.getDocument();

        document.replaceString(0, document.getTextLength(), documentText);
        editor.getCaretModel().moveToOffset(caret);
        editor.getSelectionModel().setSelection(selStart, selEnd);

        editor.getScrollingModel().scrollHorizontally(hOffset);
        editor.getScrollingModel().scrollVertically(vOffset);
      }
    };
  }
  private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection(
      CompletionProgressIndicator indicator,
      LookupElement item,
      char completionChar,
      List<LookupElement> items,
      CompletionLookupArranger.StatisticsUpdate update) {
    final Editor editor = indicator.getEditor();

    final int caretOffset = editor.getCaretModel().getOffset();
    int idEndOffset = indicator.getIdentifierEndOffset();
    if (idEndOffset < 0) {
      idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset);
    }

    CompletionAssertions.WatchingInsertionContext context = null;
    if (editor.getSelectionModel().hasBlockSelection()
        && editor.getSelectionModel().getBlockSelectionEnds().length > 0) {
      List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>();
      int idDelta = 0;
      Document document = editor.getDocument();
      int caretLine = document.getLineNumber(editor.getCaretModel().getOffset());

      for (int point : editor.getSelectionModel().getBlockSelectionEnds()) {
        insertionPoints.add(document.createRangeMarker(point, point));
        if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) {
          idDelta = idEndOffset - point;
        }
      }

      List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>();
      for (RangeMarker marker : insertionPoints) {
        if (marker.isValid()) {
          int insertionPoint = marker.getStartOffset();
          context =
              insertItem(
                  indicator,
                  item,
                  completionChar,
                  items,
                  update,
                  editor,
                  insertionPoint,
                  idDelta + insertionPoint);
          int offset = editor.getCaretModel().getOffset();
          caretsAfter.add(document.createRangeMarker(offset, offset));
        }
      }
      assert context != null;

      restoreBlockSelection(editor, caretsAfter, caretLine);

      for (RangeMarker insertionPoint : insertionPoints) {
        insertionPoint.dispose();
      }
      for (RangeMarker marker : caretsAfter) {
        marker.dispose();
      }

    } else {
      context =
          insertItem(
              indicator, item, completionChar, items, update, editor, caretOffset, idEndOffset);
    }
    return context;
  }
  private static CompletionAssertions.WatchingInsertionContext insertItem(
      final CompletionProgressIndicator indicator,
      final LookupElement item,
      final char completionChar,
      List<LookupElement> items,
      final CompletionLookupArranger.StatisticsUpdate update,
      final Editor editor,
      final PsiFile psiFile,
      final int caretOffset,
      final int idEndOffset) {
    editor.getCaretModel().moveToOffset(caretOffset);
    final int initialStartOffset = caretOffset - item.getLookupString().length();
    assert initialStartOffset >= 0
        : "negative startOffset: " + caretOffset + "; " + item.getLookupString();

    indicator
        .getOffsetMap()
        .addOffset(CompletionInitializationContext.START_OFFSET, initialStartOffset);
    indicator
        .getOffsetMap()
        .addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, caretOffset);
    indicator
        .getOffsetMap()
        .addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, idEndOffset);

    final CompletionAssertions.WatchingInsertionContext context =
        new CompletionAssertions.WatchingInsertionContext(
            indicator.getOffsetMap(), psiFile, completionChar, items, editor);
    ApplicationManager.getApplication()
        .runWriteAction(
            new Runnable() {
              @Override
              public void run() {
                if (caretOffset != idEndOffset && completionChar == Lookup.REPLACE_SELECT_CHAR) {
                  editor.getDocument().deleteString(caretOffset, idEndOffset);
                }

                assert context.getStartOffset() >= 0
                    : "stale startOffset: was "
                        + initialStartOffset
                        + "; selEnd="
                        + caretOffset
                        + "; idEnd="
                        + idEndOffset
                        + "; file="
                        + context.getFile();
                assert context.getTailOffset() >= 0
                    : "stale tail: was "
                        + initialStartOffset
                        + "; selEnd="
                        + caretOffset
                        + "; idEnd="
                        + idEndOffset
                        + "; file="
                        + context.getFile();

                item.handleInsert(context);
                Project project = indicator.getProject();
                PostprocessReformattingAspect.getInstance(project).doPostponedFormatting();

                if (context.shouldAddCompletionChar()) {
                  addCompletionChar(project, context, item, editor, indicator, completionChar);
                }
                if (!editor
                    .getCaretModel()
                    .supportsMultipleCarets()) { // done later, outside of this method
                  context.stopWatching();
                }
                editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
              }
            });
    update.addSparedChars(indicator, item, context, completionChar);
    return context;
  }
 @Override
 @Nullable
 public PsiFile getPsiFile() {
   return PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
 }
  private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection(
      final CompletionProgressIndicator indicator,
      final LookupElement item,
      final char completionChar,
      final List<LookupElement> items,
      final CompletionLookupArranger.StatisticsUpdate update) {
    final Editor editor = indicator.getEditor();

    final int caretOffset = editor.getCaretModel().getOffset();
    int idEndOffset = indicator.getIdentifierEndOffset();
    if (idEndOffset < 0) {
      idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset);
    }
    final int idEndOffsetDelta = idEndOffset - caretOffset;

    CompletionAssertions.WatchingInsertionContext context = null;
    if (editor.getSelectionModel().hasBlockSelection()
        && editor.getSelectionModel().getBlockSelectionEnds().length > 0) {
      List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>();
      int idDelta = 0;
      Document document = editor.getDocument();
      int caretLine = document.getLineNumber(editor.getCaretModel().getOffset());

      for (int point : editor.getSelectionModel().getBlockSelectionEnds()) {
        insertionPoints.add(document.createRangeMarker(point, point));
        if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) {
          idDelta = idEndOffset - point;
        }
      }

      List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>();
      for (RangeMarker marker : insertionPoints) {
        if (marker.isValid()) {
          int insertionPoint = marker.getStartOffset();
          context =
              insertItem(
                  indicator,
                  item,
                  completionChar,
                  items,
                  update,
                  editor,
                  indicator.getParameters().getOriginalFile(),
                  insertionPoint,
                  idDelta + insertionPoint);
          int offset = editor.getCaretModel().getOffset();
          caretsAfter.add(document.createRangeMarker(offset, offset));
        }
      }
      assert context != null;

      restoreBlockSelection(editor, caretsAfter, caretLine);

      for (RangeMarker insertionPoint : insertionPoints) {
        insertionPoint.dispose();
      }
      for (RangeMarker marker : caretsAfter) {
        marker.dispose();
      }

    } else if (editor.getCaretModel().supportsMultipleCarets()) {
      final List<CompletionAssertions.WatchingInsertionContext> contexts =
          new ArrayList<CompletionAssertions.WatchingInsertionContext>();
      final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
      hostEditor
          .getCaretModel()
          .runForEachCaret(
              new CaretAction() {
                @Override
                public void perform(Caret caret) {
                  PsiFile hostFile =
                      InjectedLanguageUtil.getTopLevelFile(
                          indicator.getParameters().getOriginalFile());
                  PsiFile targetFile =
                      InjectedLanguageUtil.findInjectedPsiNoCommit(hostFile, caret.getOffset());
                  Editor targetEditor =
                      InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, targetFile);
                  int targetCaretOffset = targetEditor.getCaretModel().getOffset();
                  CompletionAssertions.WatchingInsertionContext currentContext =
                      insertItem(
                          indicator,
                          item,
                          completionChar,
                          items,
                          update,
                          targetEditor,
                          targetFile == null ? hostFile : targetFile,
                          targetCaretOffset,
                          targetCaretOffset + idEndOffsetDelta);
                  contexts.add(currentContext);
                }
              },
              true);
      context = contexts.get(contexts.size() - 1);
      if (context.shouldAddCompletionChar()
          && context.getCompletionChar() != Lookup.COMPLETE_STATEMENT_SELECT_CHAR) {
        ApplicationManager.getApplication()
            .runWriteAction(
                new Runnable() {
                  @Override
                  public void run() {
                    DataContext dataContext =
                        DataManager.getInstance().getDataContext(editor.getContentComponent());
                    EditorActionManager.getInstance()
                        .getTypedAction()
                        .getHandler()
                        .execute(editor, completionChar, dataContext);
                  }
                });
      }
      for (CompletionAssertions.WatchingInsertionContext insertionContext : contexts) {
        insertionContext.stopWatching();
      }
    } else {
      context =
          insertItem(
              indicator,
              item,
              completionChar,
              items,
              update,
              editor,
              indicator.getParameters().getOriginalFile(),
              caretOffset,
              idEndOffset);
    }
    return context;
  }
  private void insertDummyIdentifier(
      final CompletionInitializationContext initContext,
      final boolean hasModifiers,
      final int invocationCount) {
    final PsiFile originalFile = initContext.getFile();
    InjectedLanguageManager manager =
        InjectedLanguageManager.getInstance(originalFile.getProject());
    final PsiFile hostFile = manager.getTopLevelFile(originalFile);
    final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(initContext.getEditor());
    final OffsetMap hostMap =
        translateOffsetMapToHost(initContext, originalFile, hostFile, hostEditor);

    final PsiFile[] hostCopy = {null};
    DocumentUtil.writeInRunUndoTransparentAction(
        new Runnable() {
          @Override
          public void run() {
            hostCopy[0] =
                createFileCopy(
                    hostFile, initContext.getStartOffset(), initContext.getSelectionEndOffset());
          }
        });

    final Document copyDocument = hostCopy[0].getViewProvider().getDocument();
    assert copyDocument != null : "no document";
    final OffsetTranslator translator =
        new OffsetTranslator(hostEditor.getDocument(), initContext.getFile(), copyDocument);

    CompletionAssertions.checkEditorValid(initContext.getEditor());
    CommandProcessor.getInstance()
        .runUndoTransparentAction(
            new Runnable() {
              @Override
              public void run() {
                ApplicationManager.getApplication()
                    .runWriteAction(
                        new Runnable() {
                          @Override
                          public void run() {
                            String dummyIdentifier = initContext.getDummyIdentifier();
                            if (StringUtil.isEmpty(dummyIdentifier)) return;

                            int startOffset =
                                hostMap.getOffset(CompletionInitializationContext.START_OFFSET);
                            int endOffset =
                                hostMap.getOffset(
                                    CompletionInitializationContext.SELECTION_END_OFFSET);
                            copyDocument.replaceString(startOffset, endOffset, dummyIdentifier);
                          }
                        });
              }
            });
    CompletionAssertions.checkEditorValid(initContext.getEditor());

    final Project project = originalFile.getProject();

    if (!synchronous) {
      if (CompletionServiceImpl.isPhase(CompletionPhase.NoCompletion.getClass())
          || !CompletionServiceImpl.assertPhase(CompletionPhase.CommittingDocuments.class)) {
        Disposer.dispose(translator);
        return;
      }

      final CompletionPhase.CommittingDocuments phase =
          (CompletionPhase.CommittingDocuments) CompletionServiceImpl.getCompletionPhase();

      CompletionAutoPopupHandler.runLaterWithCommitted(
          project,
          copyDocument,
          new Runnable() {
            @Override
            public void run() {
              if (phase.checkExpired()) {
                Disposer.dispose(translator);
                return;
              }
              doComplete(
                  initContext, hasModifiers, invocationCount, hostCopy[0], hostMap, translator);
            }
          });
    } else {
      PsiDocumentManager.getInstance(project).commitDocument(copyDocument);

      doComplete(initContext, hasModifiers, invocationCount, hostCopy[0], hostMap, translator);
    }
  }
  public final void invokeCompletion(
      @NotNull final Project project,
      @NotNull final Editor editor,
      int time,
      boolean hasModifiers,
      boolean restarted,
      @NotNull final Caret caret) {
    markCaretAsProcessed(caret);

    if (invokedExplicitly) {
      CompletionLookupArranger.applyLastCompletionStatisticsUpdate();
    }

    checkNoWriteAccess();

    CompletionAssertions.checkEditorValid(editor);

    int offset = editor.getCaretModel().getOffset();
    if (editor.isViewer() || editor.getDocument().getRangeGuard(offset, offset) != null) {
      editor.getDocument().fireReadOnlyModificationAttempt();
      CodeInsightUtilBase.showReadOnlyViewWarning(editor);
      return;
    }

    if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) {
      return;
    }

    CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
    boolean repeated =
        phase.indicator != null && phase.indicator.isRepeatedInvocation(myCompletionType, editor);
    /*
    if (repeated && isAutocompleteCommonPrefixOnInvocation() && phase.fillInCommonPrefix()) {
      return;
    }
    */

    final int newTime = phase.newCompletionStarted(time, repeated);
    if (invokedExplicitly) {
      time = newTime;
    }
    final int invocationCount = time;
    if (CompletionServiceImpl.isPhase(CompletionPhase.InsertedSingleItem.class)) {
      CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
    }
    CompletionServiceImpl.assertPhase(
        CompletionPhase.NoCompletion.getClass(), CompletionPhase.CommittingDocuments.class);

    if (invocationCount > 1 && myCompletionType == CompletionType.BASIC) {
      FeatureUsageTracker.getInstance()
          .triggerFeatureUsed(CodeCompletionFeatures.SECOND_BASIC_COMPLETION);
    }

    final CompletionInitializationContext[] initializationContext = {null};

    Runnable initCmd =
        new Runnable() {
          @Override
          public void run() {
            Runnable runnable =
                new Runnable() {
                  @Override
                  public void run() {
                    EditorUtil.fillVirtualSpaceUntilCaret(editor);
                    PsiDocumentManager.getInstance(project).commitAllDocuments();
                    CompletionAssertions.checkEditorValid(editor);

                    final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(caret, project);
                    assert psiFile != null
                        : "no PSI file: "
                            + FileDocumentManager.getInstance().getFile(editor.getDocument());
                    psiFile.putUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING, Boolean.TRUE);
                    CompletionAssertions.assertCommitSuccessful(editor, psiFile);

                    initializationContext[0] =
                        runContributorsBeforeCompletion(editor, psiFile, invocationCount, caret);
                  }
                };
            ApplicationManager.getApplication().runWriteAction(runnable);
          }
        };
    if (autopopup) {
      CommandProcessor.getInstance().runUndoTransparentAction(initCmd);
      if (!restarted && shouldSkipAutoPopup(editor, initializationContext[0].getFile())) {
        CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
        return;
      }
    } else {
      CommandProcessor.getInstance().executeCommand(project, initCmd, null, null);
    }

    insertDummyIdentifier(initializationContext[0], hasModifiers, invocationCount);
  }
  private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection(
      final CompletionProgressIndicator indicator,
      final LookupElement item,
      final char completionChar,
      final List<LookupElement> items,
      final CompletionLookupArranger.StatisticsUpdate update) {
    final Editor editor = indicator.getEditor();

    final int caretOffset = editor.getCaretModel().getOffset();
    int idEndOffset = indicator.getIdentifierEndOffset();
    if (idEndOffset < 0) {
      idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset);
    }
    final int idEndOffsetDelta = idEndOffset - caretOffset;

    CompletionAssertions.WatchingInsertionContext context = null;
    if (editor.getSelectionModel().hasBlockSelection()
        && editor.getSelectionModel().getBlockSelectionEnds().length > 0) {
      List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>();
      int idDelta = 0;
      Document document = editor.getDocument();
      int caretLine = document.getLineNumber(editor.getCaretModel().getOffset());

      for (int point : editor.getSelectionModel().getBlockSelectionEnds()) {
        insertionPoints.add(document.createRangeMarker(point, point));
        if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) {
          idDelta = idEndOffset - point;
        }
      }

      List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>();
      for (RangeMarker marker : insertionPoints) {
        if (marker.isValid()) {
          int insertionPoint = marker.getStartOffset();
          context =
              insertItem(
                  indicator,
                  item,
                  completionChar,
                  items,
                  update,
                  editor,
                  insertionPoint,
                  idDelta + insertionPoint);
          int offset = editor.getCaretModel().getOffset();
          caretsAfter.add(document.createRangeMarker(offset, offset));
        }
      }
      assert context != null;

      restoreBlockSelection(editor, caretsAfter, caretLine);

      for (RangeMarker insertionPoint : insertionPoints) {
        insertionPoint.dispose();
      }
      for (RangeMarker marker : caretsAfter) {
        marker.dispose();
      }

    } else {
      final Ref<CompletionAssertions.WatchingInsertionContext> contextRef =
          new Ref<CompletionAssertions.WatchingInsertionContext>();
      editor
          .getCaretModel()
          .runForEachCaret(
              new CaretAction() {
                @Override
                public void perform(Caret caret) {
                  CompletionAssertions.WatchingInsertionContext currentContext =
                      insertItem(
                          indicator,
                          item,
                          completionChar,
                          items,
                          update,
                          editor,
                          caret.getOffset(),
                          caret.getOffset() + idEndOffsetDelta);
                  if (caret
                      .getVisualPosition()
                      .equals(editor.getCaretModel().getPrimaryCaret().getVisualPosition())) {
                    contextRef.set(currentContext);
                  }
                }
              });
      context = contextRef.get();
    }
    return context;
  }
 private static void clearCaretMarkers(@NotNull Editor editor) {
   for (Caret caret : editor.getCaretModel().getAllCarets()) {
     caret.putUserData(CARET_PROCESSED, null);
   }
 }
  private void addListeners() {
    myEditor
        .getDocument()
        .addDocumentListener(
            new DocumentAdapter() {
              @Override
              public void documentChanged(DocumentEvent e) {
                if (!myChangeGuard && !myFinishing) {
                  hide();
                }
              }
            },
            this);

    final CaretListener caretListener =
        new CaretAdapter() {
          @Override
          public void caretPositionChanged(CaretEvent e) {
            if (!myChangeGuard && !myFinishing) {
              hide();
            }
          }
        };
    final SelectionListener selectionListener =
        new SelectionListener() {
          @Override
          public void selectionChanged(final SelectionEvent e) {
            if (!myChangeGuard && !myFinishing) {
              hide();
            }
          }
        };
    final EditorMouseListener mouseListener =
        new EditorMouseAdapter() {
          @Override
          public void mouseClicked(EditorMouseEvent e) {
            e.consume();
            hide();
          }
        };

    myEditor.getCaretModel().addCaretListener(caretListener);
    myEditor.getSelectionModel().addSelectionListener(selectionListener);
    myEditor.addEditorMouseListener(mouseListener);
    Disposer.register(
        this,
        new Disposable() {
          @Override
          public void dispose() {
            myEditor.getCaretModel().removeCaretListener(caretListener);
            myEditor.getSelectionModel().removeSelectionListener(selectionListener);
            myEditor.removeEditorMouseListener(mouseListener);
          }
        });

    JComponent editorComponent = myEditor.getContentComponent();
    if (editorComponent.isShowing()) {
      Disposer.register(
          this,
          new UiNotifyConnector(
              editorComponent,
              new Activatable() {
                @Override
                public void showNotify() {}

                @Override
                public void hideNotify() {
                  hideLookup(false);
                }
              }));
    }

    myList.addListSelectionListener(
        new ListSelectionListener() {
          private LookupElement oldItem = null;

          @Override
          public void valueChanged(@NotNull ListSelectionEvent e) {
            if (!myUpdating) {
              final LookupElement item = getCurrentItem();
              fireCurrentItemChanged(oldItem, item);
              oldItem = item;
            }
          }
        });

    new ClickListener() {
      @Override
      public boolean onClick(@NotNull MouseEvent e, int clickCount) {
        setFocusDegree(FocusDegree.FOCUSED);
        markSelectionTouched();

        if (clickCount == 2) {
          CommandProcessor.getInstance()
              .executeCommand(
                  myProject,
                  new Runnable() {
                    @Override
                    public void run() {
                      finishLookup(NORMAL_SELECT_CHAR);
                    }
                  },
                  "",
                  null);
        }
        return true;
      }
    }.installOn(myList);
  }