@Override
  public void handleInsert(InsertionContext context) {
    final Document document = context.getEditor().getDocument();
    document.replaceString(context.getStartOffset(), context.getTailOffset(), ";");
    final InsertionContext qualifierContext =
        CompletionUtil.emulateInsertion(context, context.getStartOffset(), myQualifier);
    OffsetKey oldStart = context.trackOffset(context.getStartOffset(), false);

    int start =
        CharArrayUtil.shiftForward(
            context.getDocument().getCharsSequence(), context.getStartOffset(), " \t\n");
    if (shouldParenthesizeQualifier(context.getFile(), start, qualifierContext.getTailOffset())) {
      final String space =
          CodeStyleSettingsManager.getSettings(qualifierContext.getProject())
                  .SPACE_WITHIN_PARENTHESES
              ? " "
              : "";
      document.insertString(start, "(" + space);
      document.insertString(qualifierContext.getTailOffset(), space + ")");
    }

    final char atTail = document.getCharsSequence().charAt(context.getTailOffset() - 1);
    if (atTail != ';') {
      LOG.error(
          LogMessageEx.createEvent(
              "Unexpected character",
              "atTail="
                  + atTail
                  + "\n"
                  + "offset="
                  + context.getTailOffset()
                  + "\n"
                  + "item="
                  + this
                  + "\n"
                  + "item.class="
                  + this.getClass()
                  + "\n"
                  + DebugUtil.currentStackTrace(),
              AttachmentFactory.createAttachment(context.getDocument())));
    }
    document.replaceString(context.getTailOffset() - 1, context.getTailOffset(), ".");

    CompletionUtil.emulateInsertion(getDelegate(), context.getTailOffset(), context);
    context.commitDocument();

    int formatStart = context.getOffset(oldStart);
    int formatEnd = context.getTailOffset();
    if (formatStart >= 0 && formatEnd >= 0) {
      CodeStyleManager.getInstance(context.getProject())
          .reformatText(context.getFile(), formatStart, formatEnd);
    }
  }
    @Nullable
    private PsiComment createComment(final CharSequence buffer, final CodeInsightSettings settings)
        throws IncorrectOperationException {
      myDocument.insertString(myOffset, buffer);

      PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
      CodeStyleManager.getInstance(getProject())
          .adjustLineIndent(myFile, myOffset + buffer.length() - 2);

      PsiComment comment =
          PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);

      comment = createJavaDocStub(settings, comment, getProject());
      if (comment == null) {
        return null;
      }

      CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
      CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject());
      boolean old = codeStyleSettings.ENABLE_JAVADOC_FORMATTING;
      codeStyleSettings.ENABLE_JAVADOC_FORMATTING = false;

      try {
        comment = (PsiComment) codeStyleManager.reformat(comment);
      } finally {
        codeStyleSettings.ENABLE_JAVADOC_FORMATTING = old;
      }
      PsiElement next = comment.getNextSibling();
      if (next == null && comment.getParent().getClass() == comment.getClass()) {
        next =
            comment
                .getParent()
                .getNextSibling(); // expanding chameleon comment produces comment under comment
      }
      if (next != null) {
        next =
            myFile.findElementAt(
                next.getTextRange().getStartOffset()); // maybe switch to another tree
      }
      if (next != null
          && (!FormatterUtil.containsWhiteSpacesOnly(next.getNode())
              || !next.getText().contains(LINE_SEPARATOR))) {
        int lineBreakOffset = comment.getTextRange().getEndOffset();
        myDocument.insertString(lineBreakOffset, LINE_SEPARATOR);
        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
        codeStyleManager.adjustLineIndent(myFile, lineBreakOffset + 1);
        comment =
            PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);
      }
      return comment;
    }
 @Override
 public void doApply(
     @NotNull Editor editor,
     @NotNull PySmartEnterProcessor processor,
     @NotNull PyWithStatement withStatement)
     throws IncorrectOperationException {
   final PsiElement colonToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.COLON);
   final PsiElement withToken =
       PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.WITH_KEYWORD);
   final Document document = editor.getDocument();
   if (colonToken == null && withToken != null) {
     int insertAt = withToken.getTextRange().getEndOffset();
     String textToInsert = ":";
     final PyWithItem lastItem = ArrayUtil.getLastElement(withStatement.getWithItems());
     if (lastItem == null || lastItem.getExpression() == null) {
       textToInsert = " :";
       processor.registerUnresolvedError(insertAt + 1);
     } else {
       final PyExpression expression = lastItem.getExpression();
       insertAt = expression.getTextRange().getEndOffset();
       final PsiElement asToken = PyUtil.getFirstChildOfType(lastItem, PyTokenTypes.AS_KEYWORD);
       if (asToken != null) {
         insertAt = asToken.getTextRange().getEndOffset();
         final PyExpression target = lastItem.getTarget();
         if (target != null) {
           insertAt = target.getTextRange().getEndOffset();
         } else {
           textToInsert = " :";
           processor.registerUnresolvedError(insertAt + 1);
         }
       }
     }
     document.insertString(insertAt, textToInsert);
   }
 }
  public void testSoftWrapsRecalculationInASpecificCase() throws Exception {
    configureFromFileText(
        getTestName(false) + ".java",
        "<selection>class Foo {\n"
            + "\t@Override\n"
            + "\tpublic boolean equals(Object other) {\n"
            + "\t\treturn this == other;\n"
            + "\t}\n"
            + "}</selection>");
    CodeFoldingManager.getInstance(ourProject).buildInitialFoldings(myEditor);
    configureSoftWraps(32);

    // verify initial state
    assertEquals(4, EditorUtil.getTabSize(myEditor));
    assertEquals(
        "[FoldRegion +(59:64), placeholder=' { ', FoldRegion +(85:88), placeholder=' }']",
        myEditor.getFoldingModel().toString());
    verifySoftWrapPositions(52, 85);

    Document document = myEditor.getDocument();
    for (int i = document.getLineCount() - 1; i >= 0; i--) {
      document.insertString(document.getLineStartOffset(i), "//");
    }

    verifySoftWrapPositions(58, 93);
  }
  @NotNull
  private static Document setupFileEditorAndDocument(
      @NotNull String fileName, @NotNull String fileText) throws IOException {
    EncodingProjectManager.getInstance(getProject()).setEncoding(null, CharsetToolkit.UTF8_CHARSET);
    EncodingProjectManager.getInstance(ProjectManager.getInstance().getDefaultProject())
        .setEncoding(null, CharsetToolkit.UTF8_CHARSET);
    PostprocessReformattingAspect.getInstance(ourProject).doPostponedFormatting();
    deleteVFile();
    myVFile = getSourceRoot().createChildData(null, fileName);
    VfsUtil.saveText(myVFile, fileText);
    final FileDocumentManager manager = FileDocumentManager.getInstance();
    final Document document = manager.getDocument(myVFile);
    assertNotNull("Can't create document for '" + fileName + "'", document);
    manager.reloadFromDisk(document);
    document.insertString(0, " ");
    document.deleteString(0, 1);
    myFile = getPsiManager().findFile(myVFile);
    assertNotNull(
        "Can't create PsiFile for '" + fileName + "'. Unknown file type most probably.", myFile);
    assertTrue(myFile.isPhysical());
    myEditor = createEditor(myVFile);
    myVFile.setCharset(CharsetToolkit.UTF8_CHARSET);

    PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
    return document;
  }
Beispiel #6
0
  @SuppressWarnings("ForLoopThatDoesntUseLoopVariable")
  private static void indentPlainTextBlock(
      final Document document, final int startOffset, final int endOffset, final int indentLevel) {
    CharSequence chars = document.getCharsSequence();
    int spaceEnd = CharArrayUtil.shiftForward(chars, startOffset, " \t");
    int line = document.getLineNumber(startOffset);
    if (spaceEnd > endOffset
        || indentLevel <= 0
        || line >= document.getLineCount() - 1
        || chars.charAt(spaceEnd) == '\n') {
      return;
    }

    int linesToAdjustIndent = 0;
    for (int i = line + 1; i < document.getLineCount(); i++) {
      if (document.getLineStartOffset(i) >= endOffset) {
        break;
      }
      linesToAdjustIndent++;
    }

    String indentString = StringUtil.repeatSymbol(' ', indentLevel);

    for (; linesToAdjustIndent > 0; linesToAdjustIndent--) {
      int lineStartOffset = document.getLineStartOffset(++line);
      document.insertString(lineStartOffset, indentString);
    }
  }
  @Override
  public Result preprocessEnter(
      @NotNull final PsiFile file,
      @NotNull final Editor editor,
      @NotNull final Ref<Integer> caretOffsetRef,
      @NotNull final Ref<Integer> caretAdvance,
      @NotNull final DataContext dataContext,
      final EditorActionHandler originalHandler) {
    int caretOffset = caretOffsetRef.get().intValue();
    PsiElement psiAtOffset = file.findElementAt(caretOffset);
    if (psiAtOffset != null && psiAtOffset.getTextOffset() < caretOffset) {
      ASTNode token = psiAtOffset.getNode();
      Document document = editor.getDocument();
      CharSequence text = document.getText();
      final Language language = psiAtOffset.getLanguage();
      final Commenter languageCommenter = LanguageCommenters.INSTANCE.forLanguage(language);
      final CodeDocumentationAwareCommenter commenter =
          languageCommenter instanceof CodeDocumentationAwareCommenter
              ? (CodeDocumentationAwareCommenter) languageCommenter
              : null;
      if (commenter != null && token.getElementType() == commenter.getLineCommentTokenType()) {
        final int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t");

        if (offset < document.getTextLength() && text.charAt(offset) != '\n') {
          String prefix = commenter.getLineCommentPrefix();
          assert prefix != null : "Line Comment type is set but Line Comment Prefix is null!";
          if (!StringUtil.startsWith(text, offset, prefix)) {
            if (text.charAt(caretOffset) != ' ' && !prefix.endsWith(" ")) {
              prefix += " ";
            }
            document.insertString(caretOffset, prefix);
            return Result.Default;
          } else {
            int afterPrefix = offset + prefix.length();
            if (afterPrefix < document.getTextLength() && text.charAt(afterPrefix) != ' ') {
              document.insertString(afterPrefix, " ");
              // caretAdvance.set(0);
            }
            caretOffsetRef.set(offset);
          }
          return Result.Default;
        }
      }
    }
    return Result.Continue;
  }
  public void testNestedAfter() {
    RangeMarker marker1 = createMarker("0[12345678]90123");
    Document document = marker1.getDocument();
    RangeMarker marker2 = document.createRangeMarker(2, 5);
    RangeMarker marker3 = document.createRangeMarker(3, 4);
    document.insertString(10, "x");

    assertEquals(1, marker1.getStartOffset());
    assertEquals(2, marker2.getStartOffset());
    assertEquals(3, marker3.getStartOffset());
  }
  public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) {
    setOffsets(context, offset, offset);

    final Editor editor = context.getEditor();
    final Document document = editor.getDocument();
    final String lookupString = item.getLookupString();

    document.insertString(offset, lookupString);
    editor.getCaretModel().moveToOffset(context.getTailOffset());
    PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
    item.handleInsert(context);
  }
 @Override
 public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement)
     throws IncorrectOperationException {
   if (psiElement instanceof PsiEnumConstant) {
     int insertionOffset = psiElement.getTextRange().getEndOffset();
     Document doc = editor.getDocument();
     final CharSequence text = doc.getCharsSequence();
     final int probableCommaOffset = CharArrayUtil.shiftForward(text, insertionOffset, " \t");
     if (probableCommaOffset >= text.length() || text.charAt(probableCommaOffset) != ',') {
       doc.insertString(insertionOffset, ",");
     }
   }
 }
Beispiel #11
0
  private void insertString(VirtualFile file, int offset, @NotNull CharSequence s)
      throws InterruptedException {
    final Document document = FileDocumentManager.getInstance().getDocument(file);
    assertNotNull(document);
    AccessToken token = WriteAction.start();
    try {
      document.insertString(offset, s);
    } finally {
      token.finish();
    }

    semaphore.down();
    PsiDocumentManager.getInstance(myProject).commitAllDocuments();
    FileDocumentManager.getInstance().saveAllDocuments();
    await();
  }
 public static RangeMarker insertTemporary(
     final int endOffset, final Document document, final String temporary) {
   final CharSequence chars = document.getCharsSequence();
   final int length = chars.length();
   final RangeMarker toDelete;
   if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))) {
     document.insertString(endOffset, temporary);
     toDelete = document.createRangeMarker(endOffset, endOffset + 1);
   } else if (endOffset >= length) {
     toDelete = document.createRangeMarker(length, length);
   } else {
     toDelete = document.createRangeMarker(endOffset, endOffset);
   }
   toDelete.setGreedyToLeft(true);
   toDelete.setGreedyToRight(true);
   return toDelete;
 }
 @Override
 public void actionPerformed(AnActionEvent e) {
   Project project = e.getProject();
   if (project == null) {
     return;
   }
   for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) {
     listener.beforeCheck(e);
   }
   final AnswerPlaceholder answerPlaceholder = getAnswerPlaceholder(e);
   if (answerPlaceholder == null) {
     return;
   }
   StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project);
   final StudyState studyState = new StudyState(studyEditor);
   if (answerPlaceholder.getTaskFile().getTask().hasSubtasks()) {
     StudySubtaskUtils.refreshPlaceholder(studyState.getEditor(), answerPlaceholder);
     return;
   }
   Document patternDocument =
       StudyUtils.getPatternDocument(
           answerPlaceholder.getTaskFile(), studyState.getVirtualFile().getName());
   if (patternDocument == null) {
     return;
   }
   AnswerPlaceholder.MyInitialState initialState = answerPlaceholder.getInitialState();
   int startOffset = initialState.getOffset();
   final String text =
       patternDocument.getText(new TextRange(startOffset, startOffset + initialState.getLength()));
   CommandProcessor.getInstance()
       .executeCommand(
           project,
           () ->
               ApplicationManager.getApplication()
                   .runWriteAction(
                       () -> {
                         Document document = studyState.getEditor().getDocument();
                         int offset = answerPlaceholder.getOffset();
                         document.deleteString(offset, offset + answerPlaceholder.getRealLength());
                         document.insertString(offset, text);
                       }),
           NAME,
           null);
 }
 private int doBoundCommentingAndGetShift(
     int offset,
     String commented,
     int skipLength,
     String toInsert,
     boolean skipBrace,
     TextRange selection) {
   if (commented == null
       && (offset == selection.getStartOffset()
           || offset + (skipBrace ? skipLength : 0) == selection.getEndOffset())) {
     return 0;
   }
   if (commented == null) {
     myDocument.insertString(offset + (skipBrace ? skipLength : 0), toInsert);
     return toInsert.length();
   } else {
     myDocument.replaceString(offset, offset + skipLength, commented);
     return commented.length() - skipLength;
   }
 }
    @Nullable
    private PsiComment createJavaDocStub(
        final CodeInsightSettings settings, final PsiComment comment, final Project project) {
      if (settings.JAVADOC_STUB_ON_ENTER) {
        final DocumentationProvider langDocumentationProvider =
            LanguageDocumentation.INSTANCE.forLanguage(comment.getParent().getLanguage());

        @Nullable final CodeDocumentationProvider docProvider;
        if (langDocumentationProvider instanceof CompositeDocumentationProvider) {
          docProvider =
              ((CompositeDocumentationProvider) langDocumentationProvider)
                  .getFirstCodeDocumentationProvider();
        } else {
          docProvider =
              langDocumentationProvider instanceof CodeDocumentationProvider
                  ? (CodeDocumentationProvider) langDocumentationProvider
                  : null;
        }

        if (docProvider != null) {
          if (docProvider.findExistingDocComment(comment) != comment) return comment;
          String docStub = docProvider.generateDocumentationContentStub(comment);

          if (docStub != null && docStub.length() != 0) {
            myOffset =
                CharArrayUtil.shiftForwardUntil(
                    myDocument.getCharsSequence(), myOffset, LINE_SEPARATOR);
            myOffset =
                CharArrayUtil.shiftForward(myDocument.getCharsSequence(), myOffset, LINE_SEPARATOR);
            myDocument.insertString(myOffset, docStub);
          }
        }

        PsiDocumentManager.getInstance(project).commitAllDocuments();
        return PsiTreeUtil.getNonStrictParentOfType(
            myFile.findElementAt(myOffset), PsiComment.class);
      }
      return comment;
    }
  private static void doAddImport(GoFile file, Editor editor, String pathToImport) {
    GoImportDeclarations[] ids = file.getImportDeclarations();
    Document document = editor.getDocument();

    if (ids.length == 0) {
      addImportUnderPackage(file, document, pathToImport);
      return;
    }

    GoImportDeclarations importDeclarations = ids[ids.length - 1];
    GoImportDeclaration[] imports = importDeclarations.getDeclarations();

    if (imports.length == 0) {
      addImportUnderPackage(file, document, pathToImport);
      return;
    }

    GoImportDeclaration lastImport = imports[imports.length - 1];

    PsiElement lastChild =
        getPrevSiblingIfItsWhiteSpaceOrComment(importDeclarations.getLastChild());
    if (lastChild == null) {
      addImportUnderPackage(file, document, pathToImport);
      return;
    }

    if (")".equals(lastChild.getText())) {
      document.insertString(lastChild.getTextOffset(), "\"" + pathToImport + "\"\n");
      int line = document.getLineNumber(lastChild.getTextOffset());
      reformatLines(file, editor, line, line);
    } else {
      String oldImport = lastImport.getText();
      int start = lastImport.getTextOffset();
      int end = start + lastImport.getTextLength();
      String declarations = String.format("(\n%s\n\"%s\"\n)", oldImport, pathToImport);
      document.replaceString(start, end, declarations);
      reformatPositions(file, start, start + declarations.length());
    }
  }
  public void testLazyRangeMarkers() {
    psiFile = createFile("x.txt", "xxx");

    LazyRangeMarkerFactoryImpl factory =
        (LazyRangeMarkerFactoryImpl) LazyRangeMarkerFactory.getInstance(getProject());
    VirtualFile virtualFile = psiFile.getVirtualFile();
    LazyRangeMarkerFactoryImpl.LazyMarker marker =
        (LazyRangeMarkerFactoryImpl.LazyMarker) factory.createRangeMarker(virtualFile, 0);
    WeakList<LazyRangeMarkerFactoryImpl.LazyMarker> markers =
        LazyRangeMarkerFactoryImpl.getMarkers(virtualFile);
    assertSame(marker, assertOneElement(markers));

    assertFalse(marker.isDelegated());
    assertTrue(marker.isValid());
    assertEquals(0, marker.getStartOffset());
    assertFalse(marker.isDelegated());

    marker.dispose();
    assertFalse(marker.isValid());
    assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile));

    marker = (LazyRangeMarkerFactoryImpl.LazyMarker) factory.createRangeMarker(virtualFile, 0);
    assertFalse(marker.isDelegated());
    assertTrue(marker.isValid());
    assertEquals(0, marker.getStartOffset());
    assertFalse(marker.isDelegated());

    Document document = marker.getDocument();
    document.insertString(2, "yyy");
    assertTrue(marker.isDelegated());
    assertTrue(marker.isValid());
    assertEquals(0, marker.getStartOffset());

    assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile));
    marker.dispose();
    assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile));
  }
Beispiel #18
0
  private static void indentBlockWithFormatter(
      Project project, Document document, int startOffset, int endOffset, PsiFile file) {

    // Algorithm: the main idea is to process the first line of the pasted block, adjust its indent
    // if necessary, calculate indent
    // adjustment string and apply to each line of the pasted block starting from the second one.
    //
    // We differentiate the following possible states here:
    //   --- pasted block doesn't start new line, i.e. there are non-white space symbols before it
    // at the first line.
    //      Example:
    //         old content [pasted line 1
    //                pasted line 2]
    //      Indent adjustment string is just the first line indent then.
    //
    //   --- pasted block starts with empty line(s)
    //      Example:
    //         old content [
    //            pasted line 1
    //            pasted line 2]
    //      We parse existing indents of the pasted block then, adjust its first non-blank line via
    // formatter and adjust indent
    //      of subsequent pasted lines in order to preserve old indentation.
    //
    //   --- pasted block is located at the new line and starts with white space symbols.
    //       Example:
    //          [   pasted line 1
    //                 pasted line 2]
    //       We parse existing indents of the pasted block then, adjust its first line via formatter
    // and adjust indent of the pasted lines
    //       starting from the second one in order to preserve old indentation.
    //
    //   --- pasted block is located at the new line but doesn't start with white space symbols.
    //       Example:
    //           [pasted line 1
    //         pasted line 2]
    //       We adjust the first line via formatter then and apply first line's indent to all
    // subsequent pasted lines.

    CharSequence chars = document.getCharsSequence();
    final int firstLine = document.getLineNumber(startOffset);
    final int firstLineStart = document.getLineStartOffset(firstLine);

    // There is a possible case that we paste block that ends with new line that is empty or
    // contains only white space symbols.
    // We want to preserve indent for the original document line where paste was performed.
    // Example:
    //   Original:
    //       if (test) {
    //   <caret>    }
    //
    //   Pasting: 'int i = 1;\n'
    //   Expected:
    //       if (test) {
    //           int i = 1;
    //       }
    boolean saveLastLineIndent = false;
    for (int i = endOffset - 1; i >= startOffset; i--) {
      final char c = chars.charAt(i);
      if (c == '\n') {
        saveLastLineIndent = true;
        break;
      }
      if (c != ' ' && c != '\t') {
        break;
      }
    }

    final int lastLine;
    if (saveLastLineIndent) {
      lastLine = document.getLineNumber(endOffset) - 1;
      // Remove white space symbols at the pasted text if any.
      int start = document.getLineStartOffset(lastLine + 1);
      if (start < endOffset) {
        int i = CharArrayUtil.shiftForward(chars, start, " \t");
        if (i > start) {
          i = Math.min(i, endOffset);
          document.deleteString(start, i);
        }
      }

      // Insert white space from the start line of the pasted block.
      int indentToKeepEndOffset =
          Math.min(startOffset, CharArrayUtil.shiftForward(chars, firstLineStart, " \t"));
      if (indentToKeepEndOffset > firstLineStart) {
        document.insertString(start, chars.subSequence(firstLineStart, indentToKeepEndOffset));
      }
    } else {
      lastLine = document.getLineNumber(endOffset);
    }

    final int i = CharArrayUtil.shiftBackward(chars, startOffset - 1, " \t");

    // Handle a situation when pasted block doesn't start a new line.
    if (chars.charAt(startOffset) != '\n' && i > 0 && chars.charAt(i) != '\n') {
      int firstNonWsOffset = CharArrayUtil.shiftForward(chars, firstLineStart, " \t");
      if (firstNonWsOffset > firstLineStart) {
        CharSequence toInsert = chars.subSequence(firstLineStart, firstNonWsOffset);
        for (int line = firstLine + 1; line <= lastLine; line++) {
          document.insertString(document.getLineStartOffset(line), toInsert);
        }
      }
      return;
    }

    // Sync document and PSI for correct formatting processing.
    PsiDocumentManager.getInstance(project).commitAllDocuments();
    if (file == null) {
      return;
    }
    CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);

    final int j = CharArrayUtil.shiftForward(chars, startOffset, " \t\n");
    if (j >= endOffset) {
      // Pasted text contains white space/line feed symbols only, do nothing.
      return;
    }

    final int anchorLine = document.getLineNumber(j);
    final int anchorLineStart = document.getLineStartOffset(anchorLine);
    codeStyleManager.adjustLineIndent(file, j);

    // Handle situation when pasted block starts with non-white space symbols.
    if (anchorLine == firstLine && j == startOffset) {
      int indentOffset = CharArrayUtil.shiftForward(chars, firstLineStart, " \t");
      if (indentOffset > firstLineStart) {
        CharSequence toInsert = chars.subSequence(firstLineStart, indentOffset);
        for (int line = firstLine + 1; line <= lastLine; line++) {
          document.insertString(document.getLineStartOffset(line), toInsert);
        }
      }
      return;
    }

    // Handle situation when pasted block starts from white space symbols. Assume that the pasted
    // text started at the line start,
    // i.e. correct indentation level is stored at the blocks structure.
    final int firstNonWsOffset = CharArrayUtil.shiftForward(chars, anchorLineStart, " \t");
    final int diff = firstNonWsOffset - j;
    if (diff == 0) {
      return;
    }
    if (diff > 0) {
      CharSequence toInsert = chars.subSequence(anchorLineStart, anchorLineStart + diff);
      for (int line = anchorLine + 1; line <= lastLine; line++) {
        document.insertString(document.getLineStartOffset(line), toInsert);
      }
      return;
    }

    // We've pasted text to the non-first column and exact white space between the line start and
    // caret position on the moment of paste
    // has been removed by formatter during 'adjust line indent'
    // Example:
    //       copied text:
    //                 '   line1
    //                       line2'
    //       after paste:
    //          line start -> '   I   line1
    //                              line2' (I - caret position during 'paste')
    //       formatter removed white space between the line start and caret position, so, current
    // document state is:
    //                        '   line1
    //                              line2'
    if (anchorLine == firstLine && -diff == startOffset - firstLineStart) {
      return;
    }
    if (anchorLine != firstLine || -diff > startOffset - firstLineStart) {
      final int desiredSymbolsToRemove;
      if (anchorLine == firstLine) {
        desiredSymbolsToRemove = -diff - (startOffset - firstLineStart);
      } else {
        desiredSymbolsToRemove = -diff;
      }

      for (int line = anchorLine + 1; line <= lastLine; line++) {
        int currentLineStart = document.getLineStartOffset(line);
        int currentLineIndentOffset = CharArrayUtil.shiftForward(chars, currentLineStart, " \t");
        int symbolsToRemove =
            Math.min(currentLineIndentOffset - currentLineStart, desiredSymbolsToRemove);
        if (symbolsToRemove > 0) {
          document.deleteString(currentLineStart, currentLineStart + symbolsToRemove);
        }
      }
    } else {
      CharSequence toInsert = chars.subSequence(anchorLineStart, diff + startOffset);
      for (int line = anchorLine + 1; line <= lastLine; line++) {
        document.insertString(document.getLineStartOffset(line), toInsert);
      }
    }
  }
  public final void move(Editor editor, final PsiFile file) {
    myMover.beforeMove(editor, myInfo, myIsDown);
    final Document document = editor.getDocument();
    final int start =
        StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.startLine);
    final int end = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.endLine);
    myInfo.range1 = document.createRangeMarker(start, end);

    String textToInsert = document.getCharsSequence().subSequence(start, end).toString();
    if (!StringUtil.endsWithChar(textToInsert, '\n')) textToInsert += '\n';

    final int start2 = document.getLineStartOffset(myInfo.toMove2.startLine);
    final int end2 = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove2.endLine);
    String textToInsert2 = document.getCharsSequence().subSequence(start2, end2).toString();
    if (!StringUtil.endsWithChar(textToInsert2, '\n')) textToInsert2 += '\n';
    myInfo.range2 = document.createRangeMarker(start2, end2);
    if (myInfo.range1.getStartOffset() < myInfo.range2.getStartOffset()) {
      myInfo.range1.setGreedyToLeft(true);
      myInfo.range1.setGreedyToRight(false);
      myInfo.range2.setGreedyToLeft(true);
      myInfo.range2.setGreedyToRight(true);
    } else {
      myInfo.range1.setGreedyToLeft(true);
      myInfo.range1.setGreedyToRight(true);
      myInfo.range2.setGreedyToLeft(true);
      myInfo.range2.setGreedyToRight(false);
    }

    final CaretModel caretModel = editor.getCaretModel();
    final int caretRelativePos = caretModel.getOffset() - start;
    final SelectionModel selectionModel = editor.getSelectionModel();
    final int selectionStart = selectionModel.getSelectionStart();
    final int selectionEnd = selectionModel.getSelectionEnd();
    final boolean hasSelection = selectionModel.hasSelection();

    // to prevent flicker
    caretModel.moveToOffset(0);

    // There is a possible case that the user performs, say, method move. It's also possible that
    // one (or both) of moved methods
    // are folded. We want to preserve their states then. The problem is that folding processing is
    // based on PSI element pointers
    // and the pointers behave as following during move up/down:
    //     method1() {}
    //     method2() {}
    // Pointer for the fold region from method1 points to 'method2()' now and vice versa (check
    // range markers processing on
    // document change for further information). I.e. information about fold regions statuses holds
    // the data swapped for
    // 'method1' and 'method2'. Hence, we want to apply correct 'collapsed' status.
    FoldRegion topRegion = null;
    FoldRegion bottomRegion = null;
    for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) {
      if (!foldRegion.isValid()
          || (!contains(myInfo.range1, foldRegion) && !contains(myInfo.range2, foldRegion))) {
        continue;
      }
      if (contains(myInfo.range1, foldRegion) && !contains(topRegion, foldRegion)) {
        topRegion = foldRegion;
      } else if (contains(myInfo.range2, foldRegion) && !contains(bottomRegion, foldRegion)) {
        bottomRegion = foldRegion;
      }
    }

    document.insertString(myInfo.range1.getStartOffset(), textToInsert2);
    document.deleteString(
        myInfo.range1.getStartOffset() + textToInsert2.length(), myInfo.range1.getEndOffset());

    document.insertString(myInfo.range2.getStartOffset(), textToInsert);
    int s = myInfo.range2.getStartOffset() + textToInsert.length();
    int e = myInfo.range2.getEndOffset();
    if (e > s) {
      document.deleteString(s, e);
    }

    final Project project = file.getProject();
    PsiDocumentManager.getInstance(project).commitAllDocuments();

    // Swap fold regions status if necessary.
    if (topRegion != null && bottomRegion != null) {
      final FoldRegion finalTopRegion = topRegion;
      final FoldRegion finalBottomRegion = bottomRegion;
      editor
          .getFoldingModel()
          .runBatchFoldingOperation(
              new Runnable() {
                @Override
                public void run() {
                  boolean topExpanded = finalTopRegion.isExpanded();
                  finalTopRegion.setExpanded(finalBottomRegion.isExpanded());
                  finalBottomRegion.setExpanded(topExpanded);
                }
              });
    }
    CodeFoldingManager.getInstance(project).allowFoldingOnCaretLine(editor);

    if (hasSelection) {
      restoreSelection(editor, selectionStart, selectionEnd, start, myInfo.range2.getStartOffset());
    }

    caretModel.moveToOffset(myInfo.range2.getStartOffset() + caretRelativePos);
    if (myInfo.indentTarget) {
      indentLinesIn(editor, file, document, project, myInfo.range2);
    }
    if (myInfo.indentSource) {
      indentLinesIn(editor, file, document, project, myInfo.range1);
    }

    myMover.afterMove(editor, file, myInfo, myIsDown);
    editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
  }
 private static void append(Document document, String s) {
   document.insertString(document.getTextLength(), s);
 }
  @Override
  public void handleInsert(InsertionContext context) {
    PsiVariable variable = getObject();

    Document document = context.getDocument();
    document.replaceString(context.getStartOffset(), context.getTailOffset(), variable.getName());
    context.commitDocument();

    if (variable instanceof PsiField) {
      if (willBeImported()) {
        RangeMarker toDelete =
            JavaCompletionUtil.insertTemporary(context.getTailOffset(), document, " ");
        context.commitDocument();
        final PsiReferenceExpression ref =
            PsiTreeUtil.findElementOfClassAtOffset(
                context.getFile(), context.getStartOffset(), PsiReferenceExpression.class, false);
        if (ref != null) {
          ref.bindToElementViaStaticImport(((PsiField) variable).getContainingClass());
          PostprocessReformattingAspect.getInstance(ref.getProject()).doPostponedFormatting();
        }
        if (toDelete.isValid()) {
          document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
        }
        context.commitDocument();
      } else if (shouldQualify((PsiField) variable, context)) {
        qualifyFieldReference(context, (PsiField) variable);
      }
    }

    PsiReferenceExpression ref =
        PsiTreeUtil.findElementOfClassAtOffset(
            context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false);
    if (ref != null) {
      JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(ref);
    }

    ref =
        PsiTreeUtil.findElementOfClassAtOffset(
            context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false);
    makeVariableFinalIfNeeded(context, ref);

    final char completionChar = context.getCompletionChar();
    if (completionChar == '=') {
      context.setAddCompletionChar(false);
      TailType.EQ.processTail(context.getEditor(), context.getTailOffset());
    } else if (completionChar == ','
        && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) {
      context.setAddCompletionChar(false);
      TailType.COMMA.processTail(context.getEditor(), context.getTailOffset());
      AutoPopupController.getInstance(context.getProject())
          .autoPopupParameterInfo(context.getEditor(), null);
    } else if (completionChar == ':') {
      context.setAddCompletionChar(false);
      TailType.COND_EXPR_COLON.processTail(context.getEditor(), context.getTailOffset());
    } else if (completionChar == '.') {
      AutoPopupController.getInstance(context.getProject())
          .autoPopupMemberLookup(context.getEditor(), null);
    } else if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(variable.getType())) {
      context.setAddCompletionChar(false);
      if (ref != null) {
        FeatureUsageTracker.getInstance()
            .triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
        document.insertString(ref.getTextRange().getStartOffset(), "!");
      }
    }
  }
  private void commentLine(int line, int offset, @Nullable Commenter commenter) {
    if (commenter == null) commenter = findCommenter(line);
    if (commenter == null) return;
    if (commenter instanceof SelfManagingCommenter) {
      final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;
      selfManagingCommenter.commentLine(
          line, offset, myDocument, myCommenterStateMap.get(selfManagingCommenter));
      return;
    }

    String prefix = commenter.getLineCommentPrefix();
    if (prefix != null) {
      if (commenter instanceof CommenterWithLineSuffix) {
        int endOffset = myDocument.getLineEndOffset(line);
        endOffset = CharArrayUtil.shiftBackward(myDocument.getCharsSequence(), endOffset, " \t");
        int shiftedStartOffset =
            CharArrayUtil.shiftForward(myDocument.getCharsSequence(), offset, " \t");
        String lineSuffix = ((CommenterWithLineSuffix) commenter).getLineCommentSuffix();
        if (!CharArrayUtil.regionMatches(
            myDocument.getCharsSequence(), shiftedStartOffset, prefix)) {
          if (!CharArrayUtil.regionMatches(
              myDocument.getCharsSequence(), endOffset - lineSuffix.length(), lineSuffix)) {
            myDocument.insertString(endOffset, lineSuffix);
          }
          myDocument.insertString(offset, prefix);
        }
      } else {
        myDocument.insertString(offset, prefix);
      }
    } else {
      prefix = commenter.getBlockCommentPrefix();
      String suffix = commenter.getBlockCommentSuffix();
      if (prefix == null || suffix == null) return;
      int endOffset = myDocument.getLineEndOffset(line);
      if (endOffset == offset && myStartLine != myEndLine) return;
      final int textLength = myDocument.getTextLength();
      final CharSequence chars = myDocument.getCharsSequence();
      offset = CharArrayUtil.shiftForward(chars, offset, " \t");
      if (endOffset == textLength) {
        final int shifted = CharArrayUtil.shiftBackward(chars, textLength - 1, " \t");
        if (shifted < textLength - 1) endOffset = shifted;
      } else {
        endOffset = CharArrayUtil.shiftBackward(chars, endOffset, " \t");
      }
      if (endOffset < offset || offset == textLength - 1 && line != myDocument.getLineCount() - 1) {
        return;
      }
      final String text = chars.subSequence(offset, endOffset).toString();
      final IntArrayList prefixes = new IntArrayList();
      final IntArrayList suffixes = new IntArrayList();
      final String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
      final String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
      for (int position = 0; position < text.length(); ) {
        int nearestPrefix = text.indexOf(prefix, position);
        if (nearestPrefix == -1) {
          nearestPrefix = text.length();
        }
        int nearestSuffix = text.indexOf(suffix, position);
        if (nearestSuffix == -1) {
          nearestSuffix = text.length();
        }
        if (Math.min(nearestPrefix, nearestSuffix) == text.length()) {
          break;
        }
        if (nearestPrefix < nearestSuffix) {
          prefixes.add(nearestPrefix);
          position = nearestPrefix + prefix.length();
        } else {
          suffixes.add(nearestSuffix);
          position = nearestSuffix + suffix.length();
        }
      }
      if (!(commentedSuffix == null
          && !suffixes.isEmpty()
          && offset + suffixes.get(suffixes.size() - 1) + suffix.length() >= endOffset)) {
        myDocument.insertString(endOffset, suffix);
      }
      int nearestPrefix = prefixes.size() - 1;
      int nearestSuffix = suffixes.size() - 1;
      while (nearestPrefix >= 0 || nearestSuffix >= 0) {
        if (nearestSuffix == -1
            || nearestPrefix != -1 && prefixes.get(nearestPrefix) > suffixes.get(nearestSuffix)) {
          final int position = prefixes.get(nearestPrefix);
          nearestPrefix--;
          if (commentedPrefix != null) {
            myDocument.replaceString(
                offset + position, offset + position + prefix.length(), commentedPrefix);
          } else if (position != 0) {
            myDocument.insertString(offset + position, suffix);
          }
        } else {
          final int position = suffixes.get(nearestSuffix);
          nearestSuffix--;
          if (commentedSuffix != null) {
            myDocument.replaceString(
                offset + position, offset + position + suffix.length(), commentedSuffix);
          } else if (offset + position + suffix.length() < endOffset) {
            myDocument.insertString(offset + position + suffix.length(), prefix);
          }
        }
      }
      if (!(commentedPrefix == null && !prefixes.isEmpty() && prefixes.get(0) == 0)) {
        myDocument.insertString(offset, prefix);
      }
    }
  }
  @Override
  public void invoke(
      @NotNull Project project,
      @NotNull Editor editor,
      @NotNull Caret caret,
      @NotNull PsiFile file) {
    if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;
    myProject = project;
    myEditor = editor;
    myCaret = caret;
    myFile = file;

    myDocument = editor.getDocument();

    if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) {
      return;
    }
    FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.block");
    final Commenter commenter = findCommenter(myFile, myEditor, caret);
    if (commenter == null) return;

    final String prefix;
    final String suffix;

    if (commenter instanceof SelfManagingCommenter) {
      final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;
      mySelfManagedCommenterData =
          selfManagingCommenter.createBlockCommentingState(
              caret.getSelectionStart(), caret.getSelectionEnd(), myDocument, myFile);

      if (mySelfManagedCommenterData == null) {
        mySelfManagedCommenterData = SelfManagingCommenter.EMPTY_STATE;
      }

      prefix =
          selfManagingCommenter.getBlockCommentPrefix(
              caret.getSelectionStart(), myDocument, mySelfManagedCommenterData);
      suffix =
          selfManagingCommenter.getBlockCommentSuffix(
              caret.getSelectionEnd(), myDocument, mySelfManagedCommenterData);
    } else {
      prefix = commenter.getBlockCommentPrefix();
      suffix = commenter.getBlockCommentSuffix();
    }

    if (prefix == null || suffix == null) return;

    TextRange commentedRange = findCommentedRange(commenter);
    if (commentedRange != null) {
      final int commentStart = commentedRange.getStartOffset();
      final int commentEnd = commentedRange.getEndOffset();
      int selectionStart = commentStart;
      int selectionEnd = commentEnd;
      if (myCaret.hasSelection()) {
        selectionStart = myCaret.getSelectionStart();
        selectionEnd = myCaret.getSelectionEnd();
      }
      if ((commentStart < selectionStart || commentStart >= selectionEnd)
          && (commentEnd <= selectionStart || commentEnd > selectionEnd)) {
        commentRange(selectionStart, selectionEnd, prefix, suffix, commenter);
      } else {
        uncommentRange(commentedRange, trim(prefix), trim(suffix), commenter);
      }
    } else {
      if (myCaret.hasSelection()) {
        int selectionStart = myCaret.getSelectionStart();
        int selectionEnd = myCaret.getSelectionEnd();
        if (commenter instanceof IndentedCommenter) {
          final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment();
          if (value != null && value == Boolean.TRUE) {
            selectionStart =
                myDocument.getLineStartOffset(myDocument.getLineNumber(selectionStart));
            selectionEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(selectionEnd));
          }
        }
        commentRange(selectionStart, selectionEnd, prefix, suffix, commenter);
      } else {
        EditorUtil.fillVirtualSpaceUntilCaret(editor);
        int caretOffset = myCaret.getOffset();
        if (commenter instanceof IndentedCommenter) {
          final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment();
          if (value != null && value == Boolean.TRUE) {
            final int lineNumber = myDocument.getLineNumber(caretOffset);
            final int start = myDocument.getLineStartOffset(lineNumber);
            final int end = myDocument.getLineEndOffset(lineNumber);
            commentRange(start, end, prefix, suffix, commenter);
            return;
          }
        }
        myDocument.insertString(caretOffset, prefix + suffix);
        myCaret.moveToOffset(caretOffset + prefix.length());
      }
    }
  }
    @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);
      }
    }
    private void generateJavadoc(CodeDocumentationAwareCommenter commenter)
        throws IncorrectOperationException {
      CodeInsightSettings settings = CodeInsightSettings.getInstance();
      StringBuilder buffer = new StringBuilder();
      final String docCommentLinePrefix = commenter.getDocumentationCommentLinePrefix();
      if (docCommentLinePrefix == null) {
        return;
      }

      // There are at least two approaches for completing javadoc in case there is a text between
      // current caret position and line end:
      //     1. Move that tail text below the javadoc. Use-case:
      //         Before:
      //             /**<caret>public void foo() {}
      //         After:
      //             /**
      //              */
      //             public void foo() {}
      //     2. Move the tail text inside the javadoc. Use-case:
      //          Before:
      //             /**This is <caret>javadoc description
      //          After:
      //             /** This is
      //              * javadoc description
      //              */
      // The later is most relevant when we have 'auto wrap when typing reaches right margin' option
      // set, i.e. user starts javadoc
      // and types until right margin is reached. We want the wrapped text tail to be located inside
      // javadoc and continue typing
      // inside it. So, we have a control flow branch below that does the trick.
      buffer.append(docCommentLinePrefix);
      if (DataManager.getInstance()
              .loadFromDataContext(
                  myDataContext, AutoHardWrapHandler.AUTO_WRAP_LINE_IN_PROGRESS_KEY)
          == Boolean.TRUE) {
        myDocument.insertString(myOffset, buffer);

        // We create new buffer here because the one referenced by current 'buffer' variable value
        // may be already referenced at another
        // place (e.g. 'undo' processing stuff).
        buffer =
            new StringBuilder(LINE_SEPARATOR).append(commenter.getDocumentationCommentSuffix());
        int line = myDocument.getLineNumber(myOffset);
        myOffset = myDocument.getLineEndOffset(line);
      } else {
        buffer.append(LINE_SEPARATOR);
        buffer.append(commenter.getDocumentationCommentSuffix());
      }

      PsiComment comment = createComment(buffer, settings);
      if (comment == null) {
        return;
      }

      myOffset = comment.getTextRange().getStartOffset();
      CharSequence text = myDocument.getCharsSequence();
      myOffset = CharArrayUtil.shiftForwardUntil(text, myOffset, LINE_SEPARATOR);
      myOffset = CharArrayUtil.shiftForward(text, myOffset, LINE_SEPARATOR);
      myOffset = CharArrayUtil.shiftForwardUntil(text, myOffset, docCommentLinePrefix) + 1;
      removeTrailingSpaces(myDocument, myOffset);

      if (!CodeStyleSettingsManager.getSettings(getProject()).JD_LEADING_ASTERISKS_ARE_ENABLED) {
        LOG.assertTrue(
            CharArrayUtil.regionMatches(
                myDocument.getCharsSequence(),
                myOffset - docCommentLinePrefix.length(),
                docCommentLinePrefix));
        myDocument.deleteString(myOffset - docCommentLinePrefix.length(), myOffset);
        myOffset--;
      } else {
        myDocument.insertString(myOffset, " ");
        myOffset++;
      }

      PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
    }
  private TextRange insertNestedComments(
      int startOffset,
      int endOffset,
      String commentPrefix,
      String commentSuffix,
      Commenter commenter) {
    if (commenter instanceof SelfManagingCommenter) {
      final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;
      return selfManagingCommenter.insertBlockComment(
          startOffset, endOffset, myDocument, mySelfManagedCommenterData);
    }

    String normalizedPrefix = commentPrefix.trim();
    String normalizedSuffix = commentSuffix.trim();
    IntArrayList nestedCommentPrefixes = new IntArrayList();
    IntArrayList nestedCommentSuffixes = new IntArrayList();
    String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
    String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
    CharSequence chars = myDocument.getCharsSequence();
    for (int i = startOffset; i < endOffset; ++i) {
      if (CharArrayUtil.regionMatches(chars, i, normalizedPrefix)) {
        nestedCommentPrefixes.add(i);
      } else {
        if (CharArrayUtil.regionMatches(chars, i, normalizedSuffix)) {
          nestedCommentSuffixes.add(i);
        }
      }
    }
    int shift = 0;
    if (!(commentedSuffix == null
        && !nestedCommentSuffixes.isEmpty()
        && nestedCommentSuffixes.get(nestedCommentSuffixes.size() - 1) + commentSuffix.length()
            == endOffset)) {
      myDocument.insertString(endOffset, commentSuffix);
      shift += commentSuffix.length();
    }

    // process nested comments in back order
    int i = nestedCommentPrefixes.size() - 1;
    int j = nestedCommentSuffixes.size() - 1;
    final TextRange selection = new TextRange(startOffset, endOffset);
    while (i >= 0 && j >= 0) {
      final int prefixIndex = nestedCommentPrefixes.get(i);
      final int suffixIndex = nestedCommentSuffixes.get(j);
      if (prefixIndex > suffixIndex) {
        shift +=
            doBoundCommentingAndGetShift(
                prefixIndex,
                commentedPrefix,
                normalizedPrefix.length(),
                commentSuffix,
                false,
                selection);
        --i;
      } else {
        // if (insertPos < myDocument.getTextLength() &&
        // Character.isWhitespace(myDocument.getCharsSequence().charAt(insertPos))) {
        //  insertPos = suffixIndex + commentSuffix.length();
        // }
        shift +=
            doBoundCommentingAndGetShift(
                suffixIndex,
                commentedSuffix,
                normalizedSuffix.length(),
                commentPrefix,
                true,
                selection);
        --j;
      }
    }
    while (i >= 0) {
      final int prefixIndex = nestedCommentPrefixes.get(i);
      shift +=
          doBoundCommentingAndGetShift(
              prefixIndex,
              commentedPrefix,
              normalizedPrefix.length(),
              commentSuffix,
              false,
              selection);
      --i;
    }
    while (j >= 0) {
      final int suffixIndex = nestedCommentSuffixes.get(j);
      shift +=
          doBoundCommentingAndGetShift(
              suffixIndex,
              commentedSuffix,
              normalizedSuffix.length(),
              commentPrefix,
              true,
              selection);
      --j;
    }
    if (!(commentedPrefix == null
        && !nestedCommentPrefixes.isEmpty()
        && nestedCommentPrefixes.get(0) == startOffset)) {
      myDocument.insertString(startOffset, commentPrefix);
      shift += commentPrefix.length();
    }

    RangeMarker marker = myDocument.createRangeMarker(startOffset, endOffset + shift);
    try {
      return processDocument(myDocument, marker, commenter, true);
    } finally {
      marker.dispose();
    }
  }
 protected void appendToHistoryDocument(@NotNull Document history, @NotNull String text) {
   history.insertString(history.getTextLength(), text);
 }
  /**
   * 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 boolean insertDocAsterisk(
        int lineStart,
        boolean docAsterisk,
        boolean previousLineIndentUsed,
        CodeDocumentationAwareCommenter commenter) {
      PsiElement atLineStart = myFile.findElementAt(lineStart);
      if (atLineStart == null) return false;

      final String linePrefix = commenter.getDocumentationCommentLinePrefix();
      final String docPrefix = commenter.getDocumentationCommentPrefix();

      final String text = atLineStart.getText();
      final TextRange textRange = atLineStart.getTextRange();

      if (text.equals(linePrefix)
          || text.equals(docPrefix)
          || docPrefix != null
              && text.regionMatches(
                  lineStart - textRange.getStartOffset(), docPrefix, 0, docPrefix.length())
          || linePrefix != null
              && text.regionMatches(
                  lineStart - textRange.getStartOffset(), linePrefix, 0, linePrefix.length())) {
        PsiElement element = myFile.findElementAt(myOffset);
        if (element == null) return false;

        PsiComment comment =
            element instanceof PsiComment
                ? (PsiComment) element
                : PsiTreeUtil.getParentOfType(element, PsiComment.class, false);
        if (comment != null) {
          int commentEnd = comment.getTextRange().getEndOffset();
          if (myOffset >= commentEnd) {
            docAsterisk = false;
          } else {
            removeTrailingSpaces(myDocument, myOffset);
            String toInsert =
                previousLineIndentUsed
                    ? "*"
                    : CodeDocumentationUtil.createDocCommentLine("", getProject(), commenter);
            myDocument.insertString(myOffset, toInsert);
            PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
          }
        } else {
          docAsterisk = false;
        }
      } else if (linePrefix != null
          && atLineStart instanceof PsiComment
          && ((PsiComment) atLineStart).getTokenType() == commenter.getBlockCommentTokenType()) {
        // Check if C-Style comment already uses asterisks.
        boolean usesAstersk = false;
        int commentLine = myDocument.getLineNumber(textRange.getStartOffset());
        if (commentLine < myDocument.getLineCount() - 1 && textRange.getEndOffset() >= myOffset) {
          int nextLineOffset = myDocument.getLineStartOffset(commentLine + 1);
          if (nextLineOffset < textRange.getEndOffset()) {
            final CharSequence chars = myDocument.getCharsSequence();
            nextLineOffset = CharArrayUtil.shiftForward(chars, nextLineOffset, " \t");
            usesAstersk = CharArrayUtil.regionMatches(chars, nextLineOffset, linePrefix);
          }
        }
        if (usesAstersk) {
          removeTrailingSpaces(myDocument, myOffset);
          myDocument.insertString(myOffset, linePrefix + " ");
          PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
        }
        docAsterisk = usesAstersk;
      } else {
        docAsterisk = false;
      }
      return docAsterisk;
    }
 private static void addImportUnderPackage(GoFile file, Document document, String pathToImport) {
   int insertPoint = file.getPackage().getTextRange().getEndOffset();
   document.insertString(insertPoint, String.format("\n\nimport \"%s\"", pathToImport));
 }