private void commitToOriginalInner() {
   final String text = myNewDocument.getText();
   final Map<
           PsiLanguageInjectionHost,
           Set<Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>>>
       map =
           ContainerUtil.classify(
               myMarkers.iterator(),
               new Convertor<
                   Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>,
                   PsiLanguageInjectionHost>() {
                 @Override
                 public PsiLanguageInjectionHost convert(
                     final Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> o) {
                   final PsiElement element = o.third.getElement();
                   return (PsiLanguageInjectionHost) element;
                 }
               });
   PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
   documentManager.commitDocument(myOrigDocument); // commit here and after each manipulator update
   int localInsideFileCursor = 0;
   for (PsiLanguageInjectionHost host : map.keySet()) {
     if (host == null) continue;
     String hostText = host.getText();
     ProperTextRange insideHost = null;
     StringBuilder sb = new StringBuilder();
     for (Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> entry : map.get(host)) {
       RangeMarker origMarker = entry.first; // check for validity?
       int hostOffset = host.getTextRange().getStartOffset();
       ProperTextRange localInsideHost =
           new ProperTextRange(
               origMarker.getStartOffset() - hostOffset, origMarker.getEndOffset() - hostOffset);
       RangeMarker rangeMarker = entry.second;
       ProperTextRange localInsideFile =
           new ProperTextRange(
               Math.max(localInsideFileCursor, rangeMarker.getStartOffset()),
               rangeMarker.getEndOffset());
       if (insideHost != null) {
         // append unchanged inter-markers fragment
         sb.append(
             hostText.substring(insideHost.getEndOffset(), localInsideHost.getStartOffset()));
       }
       sb.append(
           localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty()
               ? localInsideFile.substring(text)
               : "");
       localInsideFileCursor = localInsideFile.getEndOffset();
       insideHost = insideHost == null ? localInsideHost : insideHost.union(localInsideHost);
     }
     assert insideHost != null;
     ElementManipulators.getManipulator(host).handleContentChange(host, insideHost, sb.toString());
     documentManager.commitDocument(myOrigDocument);
   }
 }
 private static void commit(@NotNull PsiFile file) {
   PsiDocumentManager manager = PsiDocumentManager.getInstance(file.getProject());
   Document document = manager.getDocument(file);
   if (document != null) {
     manager.commitDocument(document);
   }
 }
  void loadFromEditor(@NotNull Editor editor) {
    assertDispatchThread();
    LOG.assertTrue(!editor.isDisposed());
    clear();

    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
    documentManager.commitDocument(editor.getDocument());
    PsiFile file = documentManager.getPsiFile(editor.getDocument());

    SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject);
    EditorFoldingInfo info = EditorFoldingInfo.get(editor);
    FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions();
    for (FoldRegion region : foldRegions) {
      if (!region.isValid()) continue;
      PsiElement element = info.getPsiElement(region);
      boolean expanded = region.isExpanded();
      boolean collapseByDefault =
          element != null
              && FoldingPolicy.isCollapseByDefault(element)
              && !FoldingUtil.caretInsideRange(editor, TextRange.create(region));
      if (collapseByDefault == expanded || element == null) {
        FoldingInfo fi = new FoldingInfo(region.getPlaceholderText(), expanded);
        if (element != null) {
          myPsiElements.add(smartPointerManager.createSmartPsiElementPointer(element, file));
          element.putUserData(FOLDING_INFO_KEY, fi);
        } else if (region.isValid()) {
          myRangeMarkers.add(region);
          region.putUserData(FOLDING_INFO_KEY, fi);
        }
      }
    }
  }
  private static void replaceByTagContent(Project project, XmlTag tagToReplace, XmlTag tagToInline)
      throws AndroidRefactoringErrorException {
    final ASTNode node = tagToInline.getNode();

    if (node == null) {
      throw new AndroidRefactoringErrorException();
    }
    final ASTNode startTagEnd = XmlChildRole.START_TAG_END_FINDER.findChild(node);
    final ASTNode closingTagStart = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(node);

    if (startTagEnd == null || closingTagStart == null) {
      throw new AndroidRefactoringErrorException();
    }
    final int contentStart = startTagEnd.getTextRange().getEndOffset();
    final int contentEnd = closingTagStart.getTextRange().getStartOffset();

    if (contentStart < 0 || contentEnd < 0 || contentStart >= contentEnd) {
      throw new AndroidRefactoringErrorException();
    }
    final PsiFile file = tagToInline.getContainingFile();

    if (file == null) {
      throw new AndroidRefactoringErrorException();
    }
    final String textToInline = file.getText().substring(contentStart, contentEnd).trim();
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    final Document document = documentManager.getDocument(tagToReplace.getContainingFile());

    if (document == null) {
      throw new AndroidRefactoringErrorException();
    }
    final TextRange range = tagToReplace.getTextRange();
    document.replaceString(range.getStartOffset(), range.getEndOffset(), textToInline);
    documentManager.commitDocument(document);
  }
  private CodeFormatterFacade getFormatterFacade(final FileViewProvider viewProvider) {
    final CodeStyleSettings styleSettings =
        CodeStyleSettingsManager.getSettings(myPsiManager.getProject());
    final PsiDocumentManager documentManager =
        PsiDocumentManager.getInstance(myPsiManager.getProject());
    final Document document = viewProvider.getDocument();
    final CodeFormatterFacade codeFormatter = new CodeFormatterFacade(styleSettings);

    documentManager.commitDocument(document);
    return codeFormatter;
  }
 public static void scheduleIndentAdjustment(
     @NotNull Project myProject, @NotNull Document myDocument, int myOffset) {
   IndentAdjusterRunnable fixer = new IndentAdjusterRunnable(myProject, myDocument, myOffset);
   PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
   if (ApplicationManager.getApplication().isUnitTestMode()) {
     documentManager.commitDocument(myDocument);
     fixer.run();
   } else {
     documentManager.performLaterWhenAllCommitted(fixer);
   }
 }
 public void doTextTest(
     @NotNull final Action action, @NotNull String text, @NotNull String textAfter)
     throws IncorrectOperationException {
   final PsiFile file = createFile("A.java", text);
   final PsiDocumentManager manager = PsiDocumentManager.getInstance(getProject());
   final Document document = manager.getDocument(file);
   if (document == null) {
     fail("Document is null");
     return;
   }
   replaceAndProcessDocument(action, text, file, document);
   assertEquals(textAfter, document.getText());
   manager.commitDocument(document);
   assertEquals(textAfter, file.getText());
 }
  @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;
  }
  // need to shorten references in type argument list
  public static void shortenReference(final PsiFile file, final int offset)
      throws IncorrectOperationException {
    Project project = file.getProject();
    final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
    Document document = manager.getDocument(file);
    if (document == null) {
      PsiUtilCore.ensureValid(file);
      LOG.error("No document for " + file);
      return;
    }

    manager.commitDocument(document);
    final PsiReference ref = file.findReferenceAt(offset);
    if (ref != null) {
      PsiElement element = ref.getElement();
      if (element != null) {
        JavaCodeStyleManager.getInstance(project).shortenClassReferences(element);
        PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
      }
    }
  }
  @NotNull
  protected String doExtract(
      final PsiDirectory targetDirectory,
      final String targetfileName,
      final T first,
      final T last,
      final Language includingLanguage)
      throws IncorrectOperationException {
    final PsiFile file = targetDirectory.createFile(targetfileName);
    Project project = targetDirectory.getProject();
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    final Document document = documentManager.getDocument(file);
    document.replaceString(0, document.getTextLength(), first.getText().trim());
    documentManager.commitDocument(document);
    CodeStyleManager.getInstance(PsiManager.getInstance(project).getProject())
        .reformat(file); // TODO: adjustLineIndent

    final String relativePath =
        PsiFileSystemItemUtil.getRelativePath(first.getContainingFile(), file);
    if (relativePath == null) throw new IncorrectOperationException("Cannot extract!");
    return relativePath;
  }
  /**
   * Inspects all lines of the given document and wraps all of them that exceed {@link
   * CodeStyleSettings#RIGHT_MARGIN right margin}.
   *
   * <p>I.e. the algorithm is to do the following for every line:
   *
   * <p>
   *
   * <pre>
   * <ol>
   *   <li>
   *      Check if the line exceeds {@link CodeStyleSettings#RIGHT_MARGIN right margin}. Go to the next line in the case of
   *      negative answer;
   *   </li>
   *   <li>Determine line wrap position; </li>
   *   <li>
   *      Perform 'smart wrap', i.e. not only wrap the line but insert additional characters over than line feed if necessary.
   *      For example consider that we wrap a single-line comment - we need to insert comment symbols on a start of the wrapped
   *      part as well. Generally, we get the same behavior as during pressing 'Enter' at wrap position during editing document;
   *   </li>
   * </ol>
   * </pre>
   *
   * @param file file that holds parsed document tree
   * @param document target document
   * @param startOffset start offset of the first line to check for wrapping (inclusive)
   * @param endOffset end offset of the first line to check for wrapping (exclusive)
   */
  private void wrapLongLinesIfNecessary(
      @NotNull final PsiFile file,
      @Nullable final Document document,
      final int startOffset,
      final int endOffset) {
    if (!mySettings.getCommonSettings(file.getLanguage()).WRAP_LONG_LINES
        || PostprocessReformattingAspect.getInstance(file.getProject())
            .isViewProviderLocked(file.getViewProvider())
        || document == null) {
      return;
    }

    final VirtualFile vFile = FileDocumentManager.getInstance().getFile(document);
    if ((vFile == null || vFile instanceof LightVirtualFile)
        && !ApplicationManager.getApplication().isUnitTestMode()) {
      // we assume that control flow reaches this place when the document is backed by a "virtual"
      // file so any changes made by
      // a formatter affect only PSI and it is out of sync with a document text
      return;
    }

    Editor editor = PsiUtilBase.findEditor(file);
    EditorFactory editorFactory = null;
    if (editor == null) {
      if (!ApplicationManager.getApplication().isDispatchThread()) {
        return;
      }
      editorFactory = EditorFactory.getInstance();
      editor = editorFactory.createEditor(document, file.getProject());
    }
    try {
      final Editor editorToUse = editor;
      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                @Override
                public void run() {
                  final CaretModel caretModel = editorToUse.getCaretModel();
                  final int caretOffset = caretModel.getOffset();
                  final RangeMarker caretMarker =
                      editorToUse.getDocument().createRangeMarker(caretOffset, caretOffset);
                  doWrapLongLinesIfNecessary(
                      editorToUse,
                      file.getProject(),
                      editorToUse.getDocument(),
                      startOffset,
                      endOffset);
                  if (caretMarker.isValid()
                      && caretModel.getOffset() != caretMarker.getStartOffset()) {
                    caretModel.moveToOffset(caretMarker.getStartOffset());
                  }
                }
              });
    } finally {
      PsiDocumentManager documentManager = PsiDocumentManager.getInstance(file.getProject());
      if (documentManager.isUncommited(document)) documentManager.commitDocument(document);
      if (editorFactory != null) {
        editorFactory.releaseEditor(editor);
      }
    }
  }
  public static int insertClassReference(
      PsiClass psiClass, PsiFile file, int startOffset, int endOffset) {
    final Project project = file.getProject();
    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    documentManager.commitAllDocuments();

    final PsiManager manager = file.getManager();

    final Document document =
        FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile());

    final PsiReference reference = file.findReferenceAt(startOffset);
    if (reference != null) {
      final PsiElement resolved = reference.resolve();
      if (resolved instanceof PsiClass) {
        if (((PsiClass) resolved).getQualifiedName() == null
            || manager.areElementsEquivalent(psiClass, resolved)) {
          return endOffset;
        }
      }
    }

    String name = psiClass.getName();
    if (name == null) {
      return endOffset;
    }

    assert document != null;
    document.replaceString(startOffset, endOffset, name);

    int newEndOffset = startOffset + name.length();
    final RangeMarker toDelete = insertTemporary(newEndOffset, document, " ");

    documentManager.commitAllDocuments();

    PsiElement element = file.findElementAt(startOffset);
    if (element instanceof PsiIdentifier) {
      PsiElement parent = element.getParent();
      if (parent instanceof PsiJavaCodeReferenceElement
          && !((PsiJavaCodeReferenceElement) parent).isQualified()
          && !(parent.getParent() instanceof PsiPackageStatement)) {
        PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement) parent;

        if (psiClass.isValid()
            && !psiClass.getManager().areElementsEquivalent(psiClass, resolveReference(ref))) {
          final boolean staticImport = ref instanceof PsiImportStaticReferenceElement;
          PsiElement newElement;
          try {
            newElement =
                staticImport
                    ? ((PsiImportStaticReferenceElement) ref).bindToTargetClass(psiClass)
                    : ref.bindToElement(psiClass);
          } catch (IncorrectOperationException e) {
            return endOffset; // can happen if fqn contains reserved words, for example
          }

          final RangeMarker rangeMarker = document.createRangeMarker(newElement.getTextRange());
          documentManager.doPostponedOperationsAndUnblockDocument(document);
          documentManager.commitDocument(document);

          newElement =
              CodeInsightUtilCore.findElementInRange(
                  file,
                  rangeMarker.getStartOffset(),
                  rangeMarker.getEndOffset(),
                  PsiJavaCodeReferenceElement.class,
                  JavaLanguage.INSTANCE);
          rangeMarker.dispose();
          if (newElement != null) {
            newEndOffset = newElement.getTextRange().getEndOffset();
            if (!(newElement instanceof PsiReferenceExpression)) {
              PsiReferenceParameterList parameterList =
                  ((PsiJavaCodeReferenceElement) newElement).getParameterList();
              if (parameterList != null) {
                newEndOffset = parameterList.getTextRange().getStartOffset();
              }
            }

            if (!staticImport
                && !psiClass
                    .getManager()
                    .areElementsEquivalent(psiClass, resolveReference((PsiReference) newElement))
                && !PsiUtil.isInnerClass(psiClass)) {
              final String qName = psiClass.getQualifiedName();
              if (qName != null) {
                document.replaceString(
                    newElement.getTextRange().getStartOffset(), newEndOffset, qName);
                newEndOffset = newElement.getTextRange().getStartOffset() + qName.length();
              }
            }
          }
        }
      }
    }

    if (toDelete.isValid()) {
      document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
    }

    return newEndOffset;
  }
  /**
   * Generates a comment if possible.
   *
   * <p>It's assumed that this method {@link PsiDocumentManager#commitDocument(Document) syncs} all
   * PSI-document changes during the processing.
   *
   * @param anchor target element for which a comment should be generated
   * @param editor target editor
   * @param commenter commenter to use
   * @param project current project
   */
  private static void generateComment(
      @NotNull PsiElement anchor,
      @NotNull Editor editor,
      @NotNull CodeDocumentationProvider documentationProvider,
      @NotNull CodeDocumentationAwareCommenter commenter,
      @NotNull Project project) {
    Document document = editor.getDocument();
    int commentStartOffset = anchor.getTextRange().getStartOffset();
    int lineStartOffset = document.getLineStartOffset(document.getLineNumber(commentStartOffset));
    if (lineStartOffset > 0 && lineStartOffset < commentStartOffset) {
      // Example:
      //    void test1() {
      //    }
      //    void test2() {
      //       <offset>
      //    }
      // We want to insert the comment at the start of the line where 'test2()' is declared.
      int nonWhiteSpaceOffset =
          CharArrayUtil.shiftBackward(document.getCharsSequence(), commentStartOffset - 1, " \t");
      commentStartOffset = Math.max(nonWhiteSpaceOffset, lineStartOffset);
    }

    int commentBodyRelativeOffset = 0;
    int caretOffsetToSet = -1;
    StringBuilder buffer = new StringBuilder();
    String commentPrefix = commenter.getDocumentationCommentPrefix();
    if (commentPrefix != null) {
      buffer.append(commentPrefix).append("\n");
      commentBodyRelativeOffset += commentPrefix.length() + 1;
    }

    String linePrefix = commenter.getDocumentationCommentLinePrefix();
    if (linePrefix != null) {
      buffer.append(linePrefix);
      commentBodyRelativeOffset += linePrefix.length();
      caretOffsetToSet = commentStartOffset + commentBodyRelativeOffset;
    }
    buffer.append("\n");
    commentBodyRelativeOffset++;

    String commentSuffix = commenter.getDocumentationCommentSuffix();
    if (commentSuffix != null) {
      buffer.append(commentSuffix).append("\n");
    }

    if (buffer.length() <= 0) {
      return;
    }

    document.insertString(commentStartOffset, buffer);
    PsiDocumentManager docManager = PsiDocumentManager.getInstance(project);
    docManager.commitDocument(document);

    Pair<PsiElement, PsiComment> pair = documentationProvider.parseContext(anchor);
    if (pair == null || pair.second == null) {
      return;
    }

    String stub = documentationProvider.generateDocumentationContentStub(pair.second);
    CaretModel caretModel = editor.getCaretModel();
    if (stub != null) {
      int insertionOffset = commentStartOffset + commentBodyRelativeOffset;
      // if (CodeStyleSettingsManager.getSettings(project).JD_ADD_BLANK_AFTER_DESCRIPTION) {
      //  buffer.setLength(0);
      //  if (linePrefix != null) {
      //    buffer.append(linePrefix);
      //  }
      //  buffer.append("\n");
      //  buffer.append(stub);
      //  stub = buffer.toString();
      // }
      document.insertString(insertionOffset, stub);
      docManager.commitDocument(document);
      pair = documentationProvider.parseContext(anchor);
    }

    if (caretOffsetToSet >= 0) {
      caretModel.moveToOffset(caretOffsetToSet);
    }

    if (pair == null || pair.second == null) {
      return;
    }

    int start = Math.min(calcStartReformatOffset(pair.first), calcStartReformatOffset(pair.second));
    int end = pair.second.getTextRange().getEndOffset();

    CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
    codeStyleManager.reformatText(anchor.getContainingFile(), start, end);

    int caretOffset = caretModel.getOffset();
    if (caretOffset > 0 && caretOffset <= document.getTextLength()) {
      char c = document.getCharsSequence().charAt(caretOffset - 1);
      if (!StringUtil.isWhiteSpace(c)) {
        document.insertString(caretOffset, " ");
        caretModel.moveToOffset(caretOffset + 1);
      }
    }
  }
  private void executeWriteActionInner(Editor editor, DataContext dataContext, Project project) {
    CodeInsightSettings settings = CodeInsightSettings.getInstance();
    if (project == null) {
      myOriginalHandler.execute(editor, dataContext);
      return;
    }
    final Document document = editor.getDocument();
    final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project);

    if (file == null) {
      myOriginalHandler.execute(editor, dataContext);
      return;
    }

    CommandProcessor.getInstance()
        .setCurrentCommandName(CodeInsightBundle.message("command.name.typing"));

    EditorModificationUtil.deleteSelectedText(editor);

    int caretOffset = editor.getCaretModel().getOffset();
    CharSequence text = document.getCharsSequence();
    int length = document.getTextLength();
    if (caretOffset < length && text.charAt(caretOffset) != '\n') {
      int offset1 = CharArrayUtil.shiftBackward(text, caretOffset, " \t");
      if (offset1 < 0 || text.charAt(offset1) == '\n') {
        int offset2 = CharArrayUtil.shiftForward(text, offset1 + 1, " \t");
        boolean isEmptyLine = offset2 >= length || text.charAt(offset2) == '\n';
        if (!isEmptyLine) { // we are in leading spaces of a non-empty line
          myOriginalHandler.execute(editor, dataContext);
          return;
        }
      }
    }

    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    documentManager.commitDocument(document);

    boolean forceIndent = false;
    boolean forceSkipIndent = false;
    Ref<Integer> caretOffsetRef = new Ref<Integer>(caretOffset);
    Ref<Integer> caretAdvanceRef = new Ref<Integer>(0);

    final EnterHandlerDelegate[] delegates = Extensions.getExtensions(EnterHandlerDelegate.EP_NAME);
    for (EnterHandlerDelegate delegate : delegates) {
      EnterHandlerDelegate.Result result =
          delegate.preprocessEnter(
              file, editor, caretOffsetRef, caretAdvanceRef, dataContext, myOriginalHandler);
      if (caretOffsetRef.get() > document.getTextLength()) {
        throw new AssertionError("Wrong caret offset change by " + delegate);
      }

      if (result == EnterHandlerDelegate.Result.Stop) {
        return;
      }
      if (result != EnterHandlerDelegate.Result.Continue) {
        if (result == EnterHandlerDelegate.Result.DefaultForceIndent) {
          forceIndent = true;
        } else if (result == EnterHandlerDelegate.Result.DefaultSkipIndent) {
          forceSkipIndent = true;
        }
        break;
      }
    }

    text = document.getCharsSequence(); // update after changes done in preprocessEnter()
    caretOffset = caretOffsetRef.get().intValue();
    boolean isFirstColumn = caretOffset == 0 || text.charAt(caretOffset - 1) == '\n';
    final boolean insertSpace =
        !isFirstColumn
            && !(caretOffset >= text.length()
                || text.charAt(caretOffset) == ' '
                || text.charAt(caretOffset) == '\t');
    editor.getCaretModel().moveToOffset(caretOffset);
    myOriginalHandler.execute(editor, dataContext);
    if (!editor.isInsertMode() || forceSkipIndent) {
      return;
    }

    if (settings.SMART_INDENT_ON_ENTER || forceIndent) {
      caretOffset += 1;
      caretOffset =
          CharArrayUtil.shiftForward(editor.getDocument().getCharsSequence(), caretOffset, " \t");
    } else {
      caretOffset = editor.getCaretModel().getOffset();
    }

    documentManager.commitAllDocuments();
    final DoEnterAction action =
        new DoEnterAction(
            file,
            editor,
            document,
            dataContext,
            caretOffset,
            !insertSpace,
            caretAdvanceRef.get(),
            project);
    action.setForceIndent(forceIndent);
    action.run();
    documentManager.commitDocument(document);
    for (EnterHandlerDelegate delegate : delegates) {
      if (delegate.postProcessEnter(file, editor, dataContext)
          == EnterHandlerDelegate.Result.Stop) {
        break;
      }
    }
    documentManager.commitDocument(document);
  }
    @Override
    public void run() {
      CaretModel caretModel = myEditor.getCaretModel();
      try {
        final CharSequence chars = myDocument.getCharsSequence();
        int i = CharArrayUtil.shiftBackwardUntil(chars, myOffset - 1, LINE_SEPARATOR) - 1;
        i = CharArrayUtil.shiftBackwardUntil(chars, i, LINE_SEPARATOR) + 1;
        if (i < 0) i = 0;
        int lineStart = CharArrayUtil.shiftForward(chars, i, " \t");
        CodeDocumentationUtil.CommentContext commentContext =
            CodeDocumentationUtil.tryParseCommentContext(myFile, chars, myOffset, lineStart);

        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(getProject());
        if (commentContext.docStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          final String text = element.getText();
          final PsiElement parent = element.getParent();

          if (text.equals(commentContext.commenter.getDocumentationCommentPrefix())
                  && isDocComment(parent, commentContext.commenter)
              || text.startsWith(commentContext.commenter.getDocumentationCommentPrefix())
                  && element instanceof PsiComment) {
            PsiComment comment =
                isDocComment(parent, commentContext.commenter)
                    ? (PsiComment) parent
                    : (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();

            if (myOffset >= commentEnd) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                generateJavadoc(commentContext.commenter);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        } else if (commentContext.cStyleStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          if (element instanceof PsiComment
              && commentContext.commenter.getBlockCommentTokenType()
                  == ((PsiComment) element).getTokenType()) {
            final PsiComment comment = (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();
            if (myOffset >= commentEnd && myOffset < myFile.getTextRange().getEndOffset()) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                final int currentEndOfLine = CharArrayUtil.shiftForwardUntil(chars, myOffset, "\n");
                myDocument.insertString(
                    currentEndOfLine, " " + commentContext.commenter.getBlockCommentSuffix());
                int lstart = CharArrayUtil.shiftBackwardUntil(chars, myOffset, "\n");
                myDocument.insertString(currentEndOfLine, chars.subSequence(lstart, myOffset));
                psiDocumentManager.commitDocument(myDocument);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        }

        String indentInsideJavadoc = null;
        if (myOffset < myDocument.getTextLength()) {
          final int line = myDocument.getLineNumber(myOffset);
          if (line > 0 && (commentContext.docAsterisk || commentContext.docStart)) {
            indentInsideJavadoc =
                CodeDocumentationUtil.getIndentInsideJavadoc(
                    myDocument, myDocument.getLineStartOffset(line - 1));
          }
        }

        if (commentContext.docAsterisk) {
          commentContext.docAsterisk =
              insertDocAsterisk(
                  commentContext.lineStart,
                  commentContext.docAsterisk,
                  !StringUtil.isEmpty(indentInsideJavadoc),
                  commentContext.commenter);
        }

        boolean docIndentApplied = false;
        CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance();
        if (codeInsightSettings.SMART_INDENT_ON_ENTER
            || myForceIndent
            || commentContext.docStart
            || commentContext.docAsterisk
            || commentContext.slashSlash) {
          final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
          myOffset = codeStyleManager.adjustLineIndent(myFile, myOffset);
          psiDocumentManager.commitAllDocuments();

          if (!StringUtil.isEmpty(indentInsideJavadoc) && myOffset < myDocument.getTextLength()) {
            myDocument.insertString(myOffset + 1, indentInsideJavadoc);
            myOffset += indentInsideJavadoc.length();
            docIndentApplied = true;
          }

          if (myForceIndent && indentInsideJavadoc != null) {
            int indentSize =
                CodeStyleSettingsManager.getSettings(myProject).getIndentSize(myFile.getFileType());
            myDocument.insertString(myOffset + 1, StringUtil.repeatSymbol(' ', indentSize));
            myCaretAdvance += indentSize;
          }
        }

        if ((commentContext.docAsterisk || commentContext.docStart || commentContext.slashSlash)
            && !docIndentApplied) {
          if (myInsertSpace) {
            if (myOffset == myDocument.getTextLength()) {
              myDocument.insertString(myOffset, " ");
            }
            myDocument.insertString(myOffset + 1, " ");
          }

          final char c = myDocument.getCharsSequence().charAt(myOffset);
          if (c != '\n') {
            myOffset += 1;
          }
        }

        if ((commentContext.docAsterisk || commentContext.slashSlash) && !commentContext.docStart) {
          myCaretAdvance +=
              commentContext.slashSlash
                  ? commentContext.commenter.getLineCommentPrefix().length()
                  : 1;
        }
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }

      myOffset = Math.min(myOffset, myDocument.getTextLength());
      caretModel.moveToOffset(myOffset);
      myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      myEditor.getSelectionModel().removeSelection();
      if (myCaretAdvance != 0) {
        LogicalPosition caretPosition = caretModel.getLogicalPosition();
        LogicalPosition pos =
            new LogicalPosition(caretPosition.line, caretPosition.column + myCaretAdvance);
        caretModel.moveToLogicalPosition(pos);
      }
    }