@Nullable
 private static String addReadOnly(@Nullable String title, @Nullable Editor editor) {
   if (editor == null || title == null) {
     return title;
   }
   boolean readonly = editor.isViewer() || !editor.getDocument().isWritable();
   if (readonly) {
     title += " " + DiffBundle.message("diff.content.read.only.content.title.suffix");
   }
   return title;
 }
  @Override
  public final void execute(final Editor editor, final DataContext dataContext) {
    if (editor.isViewer()) return;

    if (dataContext != null) {
      Project project = CommonDataKeys.PROJECT.getData(dataContext);
      if (project != null
          && !FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project))
        return;
    }

    ApplicationManager.getApplication()
        .runWriteAction(
            new DocumentRunnable(editor.getDocument(), editor.getProject()) {
              @Override
              public void run() {
                final Document doc = editor.getDocument();
                final SelectionModel selectionModel = editor.getSelectionModel();
                if (selectionModel.hasBlockSelection()) {
                  RangeMarker guard = selectionModel.getBlockSelectionGuard();
                  if (guard != null) {
                    DocumentEvent evt =
                        new MockDocumentEvent(
                            editor.getDocument(), editor.getCaretModel().getOffset());
                    ReadOnlyFragmentModificationException e =
                        new ReadOnlyFragmentModificationException(evt, guard);
                    EditorActionManager.getInstance()
                        .getReadonlyFragmentModificationHandler(doc)
                        .handle(e);
                    return;
                  }
                }

                doc.startGuardedBlockChecking();
                try {
                  executeWriteAction(editor, dataContext);
                } catch (ReadOnlyFragmentModificationException e) {
                  EditorActionManager.getInstance()
                      .getReadonlyFragmentModificationHandler(doc)
                      .handle(e);
                } finally {
                  doc.stopGuardedBlockChecking();
                }
              }
            });
  }
  @Override
  public boolean isEnabled(Editor editor, DataContext dataContext) {
    if (editor.isViewer() || editor.isOneLineMode()) return false;
    final Project project = editor.getProject();
    if (project == null || project.isDisposed()) return false;
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    final Document document = editor.getDocument();
    documentManager.commitDocument(document);
    PsiFile psiFile = documentManager.getPsiFile(document);
    PsiFile file = getRoot(psiFile, editor);
    if (file == null) return false;
    final MoverWrapper mover = getSuitableMover(editor, file);
    if (mover == null || mover.getInfo().toMove2 == null) return false;
    final int maxLine = editor.offsetToLogicalPosition(editor.getDocument().getTextLength()).line;
    final LineRange range = mover.getInfo().toMove;
    if (range.startLine == 0 && !isDown) return false;

    return range.endLine <= maxLine || !isDown;
  }
    @Override
    public void execute(
        @NotNull final Editor editor, char charTyped, @NotNull DataContext dataContext) {
      if (editor.isViewer()) return;

      Document doc = editor.getDocument();
      Project project = CommonDataKeys.PROJECT.getData(dataContext);
      if (!FileDocumentManager.getInstance().requestWriting(doc, project)) {
        return;
      }

      doc.startGuardedBlockChecking();
      try {
        final String str = String.valueOf(charTyped);
        CommandProcessor.getInstance()
            .setCurrentCommandName(EditorBundle.message("typing.in.editor.command.name"));
        EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, str, true);
      } catch (ReadOnlyFragmentModificationException e) {
        EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
      } finally {
        doc.stopGuardedBlockChecking();
      }
    }
  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);
  }