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 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;
 }
  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);
    }
  }
  private void doComplete(
      CompletionInitializationContext initContext,
      boolean hasModifiers,
      int invocationCount,
      PsiFile hostCopy,
      OffsetMap hostMap,
      OffsetTranslator translator) {
    final Editor editor = initContext.getEditor();
    CompletionAssertions.checkEditorValid(editor);

    CompletionContext context =
        createCompletionContext(
            hostCopy,
            hostMap.getOffset(CompletionInitializationContext.START_OFFSET),
            hostMap,
            initContext.getFile());
    LookupImpl lookup = obtainLookup(editor);
    CompletionParameters parameters = createCompletionParameters(invocationCount, context, editor);

    CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
    if (phase instanceof CompletionPhase.CommittingDocuments) {
      if (phase.indicator != null) {
        phase.indicator.closeAndFinish(false);
      }
      ((CompletionPhase.CommittingDocuments) phase).replaced = true;
    } else {
      CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
    }

    final Semaphore freezeSemaphore = new Semaphore();
    freezeSemaphore.down();
    final CompletionProgressIndicator indicator =
        new CompletionProgressIndicator(
            editor,
            parameters,
            this,
            freezeSemaphore,
            initContext.getOffsetMap(),
            hasModifiers,
            lookup);
    Disposer.register(indicator, hostMap);
    Disposer.register(indicator, context.getOffsetMap());
    Disposer.register(indicator, translator);

    CompletionServiceImpl.setCompletionPhase(
        synchronous
            ? new CompletionPhase.Synchronous(indicator)
            : new CompletionPhase.BgCalculation(indicator));

    final AtomicReference<LookupElement[]> data = indicator.startCompletion(initContext);

    if (!synchronous) {
      return;
    }

    if (freezeSemaphore.waitFor(2000)) {
      final LookupElement[] allItems = data.get();
      if (allItems != null
          && !indicator.isRunning()
          && !indicator
              .isCanceled()) { // the completion is really finished, now we may auto-insert or show
        // lookup
        completionFinished(
            initContext.getStartOffset(),
            initContext.getSelectionEndOffset(),
            indicator,
            allItems,
            hasModifiers);
        checkNotSync(indicator, allItems);
        return;
      }
    }

    CompletionServiceImpl.setCompletionPhase(new CompletionPhase.BgCalculation(indicator));
    indicator.showLookup();
  }
  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 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;
  }