private VerticalInfo createVerticalInfo(LogicalPosition position) {
    Document document = myEditor.getDocument();
    int logicalLine = position.line;
    if (logicalLine >= document.getLineCount()) {
      logicalLine = Math.max(0, document.getLineCount() - 1);
    }
    int startOffset = document.getLineStartOffset(logicalLine);
    int endOffset = document.getLineEndOffset(logicalLine);

    // There is a possible case that active logical line is represented on multiple lines due to
    // soft wraps processing.
    // We want to highlight those visual lines as 'active' then, so, we calculate 'y' position for
    // the logical line start
    // and height in accordance with the number of occupied visual lines.
    VisualPosition visualPosition =
        myEditor.offsetToVisualPosition(document.getLineStartOffset(logicalLine));
    int y = myEditor.visualPositionToXY(visualPosition).y;
    int lineHeight = myEditor.getLineHeight();
    int height = lineHeight;
    List<? extends SoftWrap> softWraps =
        myEditor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffset);
    for (SoftWrap softWrap : softWraps) {
      height += StringUtil.countNewLines(softWrap.getText()) * lineHeight;
    }

    return new VerticalInfo(y, height);
  }
  public void testPastingInsideSelection() throws IOException {
    String text =
        "this is line number 0\n"
            + "this is line number 1\n"
            + "this is line number 2\n"
            + "this is line number 3\n"
            + "this is line number 4\n"
            + "this is line number 5\n"
            + "this is line number 6\n"
            + "this is the last line";

    init(100, text);
    int lineToSelect = 4;
    myEditor.getCaretModel().moveToOffset(text.indexOf("number " + lineToSelect));
    Document document = myEditor.getDocument();

    int startOffset = document.getLineStartOffset(lineToSelect);
    int endOffset = document.getLineEndOffset(lineToSelect);
    myEditor.getSelectionModel().setSelection(startOffset, endOffset);

    VisualPosition positionBefore =
        myEditor.offsetToVisualPosition(document.getLineStartOffset(lineToSelect + 1));
    List<SoftWrap> softWrapsBefore = new ArrayList<>(getSoftWrapModel().getRegisteredSoftWraps());

    copy();
    paste();

    assertEquals(
        positionBefore,
        myEditor.offsetToVisualPosition(document.getLineStartOffset(lineToSelect + 1)));
    assertEquals(softWrapsBefore, getSoftWrapModel().getRegisteredSoftWraps());
  }
Example #3
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);
    }
  }
Example #4
0
  public void testRemoveHugeLogicalLineThatLaysBeforeSoftWrappedLines() throws IOException {
    String text =
        "short line\n"
            + "this is a long line that is expected to be soft wrapped into more than one or even two visual lines\n"
            + "1. just a line that is long enough to be soft wrapped\n"
            + "2. just a line that is long enough to be soft wrapped\n"
            + "3. just a line that is long enough to be soft wrapped\n"
            + "4. just a line that is long enough to be soft wrapped";

    init(15, text);
    Document document = myEditor.getDocument();
    int start = document.getLineStartOffset(1);
    int end = document.getLineEndOffset(1) + 1;
    int visualLinesToRemove = getSoftWrapModel().getSoftWrapsForLine(1).size() + 1;

    List<VisualPosition> positionsBefore = new ArrayList<VisualPosition>();
    for (int i = end; i < text.length(); i++) {
      positionsBefore.add(myEditor.offsetToVisualPosition(i));
    }
    Collections.reverse(positionsBefore);

    myEditor.getSelectionModel().setSelection(start, end);
    delete();

    // Check that all remembered positions are just shifted to expected number of visual lines.
    for (int i = start; i < document.getTextLength(); i++) {
      VisualPosition position = positionsBefore.remove(positionsBefore.size() - 1);
      assertEquals(
          new VisualPosition(position.line - visualLinesToRemove, position.column),
          myEditor.offsetToVisualPosition(i));
    }
  }
  private void doIndentCommenting(Commenter commenter) {
    CharSequence chars = myDocument.getCharsSequence();
    final FileType fileType = myFile.getFileType();
    Indent minIndent =
        computeMinIndent(myStartLine, myEndLine, chars, myCodeStyleManager, fileType);

    for (int line = myEndLine; line >= myStartLine; line--) {
      int lineStart = myDocument.getLineStartOffset(line);
      int offset = lineStart;
      final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
      try {
        while (true) {
          String space = buffer.toString();
          Indent indent = myCodeStyleManager.getIndent(space, fileType);
          if (indent.isGreaterThan(minIndent) || indent.equals(minIndent)) break;
          char c = chars.charAt(offset);
          if (c != ' ' && c != '\t') {
            String newSpace = myCodeStyleManager.fillIndent(minIndent, fileType);
            myDocument.replaceString(lineStart, offset, newSpace);
            offset = lineStart + newSpace.length();
            break;
          }
          buffer.append(c);
          offset++;
        }
      } finally {
        StringBuilderSpinAllocator.dispose(buffer);
      }
      commentLine(line, offset, commenter);
    }
  }
  public CaretModelImpl(EditorImpl editor) {
    myEditor = editor;
    myLogicalCaret = new LogicalPosition(0, 0);
    myVisibleCaret = new VisualPosition(0, 0);
    myCaretInfo = new VerticalInfo(0, 0);
    myOffset = 0;
    myVisualLineStart = 0;
    Document doc = editor.getDocument();
    myVisualLineEnd =
        doc.getLineCount() > 1
            ? doc.getLineStartOffset(1)
            : doc.getLineCount() == 0 ? 0 : doc.getLineEndOffset(0);
    DocumentBulkUpdateListener bulkUpdateListener =
        new DocumentBulkUpdateListener() {
          @Override
          public void updateStarted(Document doc) {
            if (doc != myEditor.getDocument()) return;
            savedBeforeBulkCaretMarker = doc.createRangeMarker(myOffset, myOffset);
          }

          @Override
          public void updateFinished(Document doc) {
            if (doc != myEditor.getDocument() || myIsInUpdate) return;
            if (savedBeforeBulkCaretMarker != null && savedBeforeBulkCaretMarker.isValid()) {
              moveToOffset(savedBeforeBulkCaretMarker.getStartOffset());
            }
            releaseBulkCaretMarker();
          }
        };
    ApplicationManager.getApplication()
        .getMessageBus()
        .connect(this)
        .subscribe(DocumentBulkUpdateListener.TOPIC, bulkUpdateListener);
  }
 private boolean isLineEmpty(final int line) {
   final CharSequence chars = myDocument.getCharsSequence();
   int start = myDocument.getLineStartOffset(line);
   int end = Math.min(myDocument.getLineEndOffset(line), myDocument.getTextLength() - 1);
   for (int i = start; i <= end; i++) {
     if (!Character.isWhitespace(chars.charAt(i))) return false;
   }
   return true;
 }
  private static void indentLinesIn(
      final Editor editor,
      final PsiFile file,
      final Document document,
      final Project project,
      RangeMarker range) {
    final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
    int line1 = editor.offsetToLogicalPosition(range.getStartOffset()).line;
    int line2 = editor.offsetToLogicalPosition(range.getEndOffset()).line;

    while (!lineContainsNonSpaces(document, line1) && line1 < line2) line1++;
    while (!lineContainsNonSpaces(document, line2) && line1 < line2) line2--;

    final FileViewProvider provider = file.getViewProvider();
    PsiFile rootToAdjustIndentIn = provider.getPsi(provider.getBaseLanguage());
    codeStyleManager.adjustLineIndent(
        rootToAdjustIndentIn,
        new TextRange(document.getLineStartOffset(line1), document.getLineStartOffset(line2)));
  }
Example #9
0
 private boolean isBlankLine(int line, CharSequence chars) {
   Document document = myDocument;
   if (document == null) {
     return true;
   }
   int startOffset = document.getLineStartOffset(line);
   int endOffset = document.getLineEndOffset(line);
   return CharArrayUtil.shiftForward(chars, startOffset, endOffset, " \t")
       >= myDocument.getLineEndOffset(line);
 }
 private static boolean lineContainsNonSpaces(final Document document, final int line) {
   if (line >= document.getLineCount()) {
     return false;
   }
   int lineStartOffset = document.getLineStartOffset(line);
   int lineEndOffset = document.getLineEndOffset(line);
   @NonNls
   String text =
       document.getCharsSequence().subSequence(lineStartOffset, lineEndOffset).toString();
   return text.trim().length() != 0;
 }
 private int getCommentStart(int line) {
   int offset = myDocument.getLineStartOffset(line);
   CharSequence chars = myDocument.getCharsSequence();
   offset = CharArrayUtil.shiftForward(chars, offset, " \t");
   final Commenter commenter = findCommenter(line);
   if (commenter == null) return -1;
   String prefix = commenter.getLineCommentPrefix();
   if (prefix == null) prefix = commenter.getBlockCommentPrefix();
   if (prefix == null) return -1;
   return CharArrayUtil.regionMatches(chars, offset, prefix) ? offset : -1;
 }
 @Nullable
 private static String getPropertyName(@NotNull Document document, int line) {
   int startOffset = document.getLineStartOffset(line);
   int endOffset =
       StringUtil.indexOf(
           document.getCharsSequence(), '=', startOffset, document.getLineEndOffset(line));
   if (endOffset <= startOffset) {
     return null;
   }
   String propertyName =
       document.getCharsSequence().subSequence(startOffset, endOffset).toString().trim();
   return propertyName.isEmpty() ? null : propertyName;
 }
  private static void indentLinesIn(
      final Editor editor,
      final PsiFile file,
      final Document document,
      final Project project,
      RangeMarker range) {
    final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
    int line1 = editor.offsetToLogicalPosition(range.getStartOffset()).line;
    int line2 = editor.offsetToLogicalPosition(range.getEndOffset()).line;

    /*    if (PsiUtil.isInJspFile(file)) {
      // This version is slow because of each moved line cause commit
      // and right now we unable to fix JSP formatter quickly
      // TODO: remove this code
      for (int line = line1; line <= line2; line++) {
        if (lineContainsNonSpaces(document, line)) {
          int lineStart = document.getLineStartOffset(line);
          codeStyleManager.adjustLineIndent(document, lineStart);
        }
      }
    }
    else {*/

    while (!lineContainsNonSpaces(document, line1) && line1 <= line2) line1++;
    while (!lineContainsNonSpaces(document, line2) && line2 > line1) line2--;

    try {
      final FileViewProvider provider = file.getViewProvider();
      PsiFile rootToAdjustIndentIn = provider.getPsi(provider.getBaseLanguage());
      codeStyleManager.adjustLineIndent(
          rootToAdjustIndentIn,
          new TextRange(document.getLineStartOffset(line1), document.getLineStartOffset(line2)));
    } catch (IncorrectOperationException ex) {
      throw new RuntimeException(ex);
    }
    /* } */
  }
 @Override
 @NotNull
 public List<? extends SoftWrap> getSoftWrapsForLine(int documentLine) {
   if (!isSoftWrappingEnabled() || documentLine < 0) {
     return Collections.emptyList();
   }
   Document document = myEditor.getDocument();
   int start = document.getLineStartOffset(documentLine);
   int end = document.getLineEndOffset(documentLine);
   return getSoftWrapsForRange(
       start,
       end
           + 1 /* it's theoretically possible that soft wrap is registered just before the line feed,
                * hence, we add '1' here assuming that end line offset points to line feed symbol */);
 }
  private boolean isLineCommented(
      final int line, final CharSequence chars, final Commenter commenter) {
    boolean commented;
    int lineEndForBlockCommenting = -1;
    int lineStart = myDocument.getLineStartOffset(line);
    lineStart = CharArrayUtil.shiftForward(chars, lineStart, " \t");

    if (commenter instanceof SelfManagingCommenter) {
      final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;
      commented =
          selfManagingCommenter.isLineCommented(
              line, lineStart, myDocument, myCommenterStateMap.get(selfManagingCommenter));
    } else {
      String prefix = commenter.getLineCommentPrefix();

      if (prefix != null) {
        commented =
            CharArrayUtil.regionMatches(chars, lineStart, prefix)
                || prefix.endsWith(" ")
                    && CharArrayUtil.regionMatches(chars, lineStart, prefix.trim() + "\n");
      } else {
        prefix = commenter.getBlockCommentPrefix();
        String suffix = commenter.getBlockCommentSuffix();
        final int textLength = myDocument.getTextLength();
        lineEndForBlockCommenting = myDocument.getLineEndOffset(line);
        if (lineEndForBlockCommenting == textLength) {
          final int shifted = CharArrayUtil.shiftBackward(chars, textLength - 1, " \t");
          if (shifted < textLength - 1) lineEndForBlockCommenting = shifted;
        } else {
          lineEndForBlockCommenting =
              CharArrayUtil.shiftBackward(chars, lineEndForBlockCommenting, " \t");
        }
        commented =
            lineStart == lineEndForBlockCommenting && myStartLine != myEndLine
                || CharArrayUtil.regionMatches(chars, lineStart, prefix)
                    && CharArrayUtil.regionMatches(
                        chars, lineEndForBlockCommenting - suffix.length(), suffix);
      }
    }

    if (commented) {
      myStartOffsets[line - myStartLine] = lineStart;
      myEndOffsets[line - myStartLine] = lineEndForBlockCommenting;
    }

    return commented;
  }
  @Nullable
  private Commenter findCommenter(final int line) {
    final FileType fileType = myFile.getFileType();
    if (fileType instanceof AbstractFileType) {
      return ((AbstractFileType) fileType).getCommenter();
    }

    int lineStartOffset = myDocument.getLineStartOffset(line);
    int lineEndOffset = myDocument.getLineEndOffset(line) - 1;
    final CharSequence charSequence = myDocument.getCharsSequence();
    lineStartOffset = CharArrayUtil.shiftForward(charSequence, lineStartOffset, " \t");
    lineEndOffset =
        CharArrayUtil.shiftBackward(charSequence, lineEndOffset < 0 ? 0 : lineEndOffset, " \t");
    final Language lineStartLanguage = PsiUtilBase.getLanguageAtOffset(myFile, lineStartOffset);
    final Language lineEndLanguage = PsiUtilBase.getLanguageAtOffset(myFile, lineEndOffset);
    return CommentByBlockCommentHandler.getCommenter(
        myFile, myEditor, lineStartLanguage, lineEndLanguage);
  }
  private static void logInitial(
      @NotNull Editor editor,
      @NotNull int[] startOffsets,
      @NotNull int[] endOffsets,
      int indentSymbolsToStrip,
      int firstLineStartOffset) {
    if (!Registry.is("editor.richcopy.debug")) {
      return;
    }

    StringBuilder buffer = new StringBuilder();
    Document document = editor.getDocument();
    CharSequence text = document.getCharsSequence();
    for (int i = 0; i < startOffsets.length; i++) {
      int start = startOffsets[i];
      int lineStart = document.getLineStartOffset(document.getLineNumber(start));
      int end = endOffsets[i];
      int lineEnd = document.getLineEndOffset(document.getLineNumber(end));
      buffer
          .append("    region #")
          .append(i)
          .append(": ")
          .append(start)
          .append('-')
          .append(end)
          .append(", text at range ")
          .append(lineStart)
          .append('-')
          .append(lineEnd)
          .append(": \n'")
          .append(text.subSequence(lineStart, lineEnd))
          .append("'\n");
    }
    if (buffer.length() > 0) {
      buffer.setLength(buffer.length() - 1);
    }
    LOG.info(
        String.format(
            "Preparing syntax-aware text. Given: %s selection, indent symbols to strip=%d, first line start offset=%d, selected text:%n%s",
            startOffsets.length > 1 ? "block" : "regular",
            indentSymbolsToStrip,
            firstLineStartOffset,
            buffer));
  }
  public static String getNewText(PsiElement elt) {
    Project project = elt.getProject();
    PsiFile psiFile = getContainingFile(elt);

    final Document doc = PsiDocumentManager.getInstance(project).getDocument(psiFile);
    if (doc == null) return null;

    final ImplementationTextSelectioner implementationTextSelectioner =
        LanguageImplementationTextSelectioner.INSTANCE.forLanguage(elt.getLanguage());
    int start = implementationTextSelectioner.getTextStartOffset(elt);
    final int end = implementationTextSelectioner.getTextEndOffset(elt);

    final int lineStart = doc.getLineStartOffset(doc.getLineNumber(start));
    final int lineEnd =
        end < doc.getTextLength()
            ? doc.getLineEndOffset(doc.getLineNumber(end))
            : doc.getTextLength();
    return doc.getCharsSequence().subSequence(lineStart, lineEnd).toString();
  }
  private int yPositionToOffset(int y, boolean beginLine) {
    if (myEditorScrollbarTop == -1 || myEditorTargetHeight == -1) {
      recalcEditorDimensions();
    }
    final int safeY = Math.max(0, y - myEditorScrollbarTop);
    VisualPosition visual;
    if (myEditorSourceHeight < myEditorTargetHeight) {
      visual = myEditor.xyToVisualPosition(new Point(0, safeY));
    } else {
      float fraction = Math.max(0, Math.min(1, safeY / (float) myEditorTargetHeight));
      final int lineCount = myEditorSourceHeight / myEditor.getLineHeight();
      visual = new VisualPosition((int) (fraction * lineCount), 0);
    }
    int line = myEditor.visualToLogicalPosition(visual).line;
    Document document = myEditor.getDocument();
    if (line < 0) return 0;
    if (line >= document.getLineCount()) return document.getTextLength();

    return beginLine ? document.getLineStartOffset(line) : document.getLineEndOffset(line);
  }
 private Indent computeMinIndent(
     int line1,
     int line2,
     CharSequence chars,
     CodeStyleManager codeStyleManager,
     FileType fileType) {
   Indent minIndent = CommentUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType);
   if (line1 > 0) {
     int commentOffset = getCommentStart(line1 - 1);
     if (commentOffset >= 0) {
       int lineStart = myDocument.getLineStartOffset(line1 - 1);
       String space = chars.subSequence(lineStart, commentOffset).toString();
       Indent indent = codeStyleManager.getIndent(space, fileType);
       minIndent = minIndent != null ? indent.min(minIndent) : indent;
     }
   }
   if (minIndent == null) {
     minIndent = codeStyleManager.zeroIndent();
   }
   return minIndent;
 }
  public static String calcStringToFillVirtualSpace(Editor editor, int afterLineEnd) {
    final Project project = editor.getProject();
    StringBuilder buf = new StringBuilder();
    final Document doc = editor.getDocument();
    final int caretOffset = editor.getCaretModel().getOffset();
    boolean atLineStart =
        caretOffset >= doc.getTextLength()
            || doc.getLineStartOffset(doc.getLineNumber(caretOffset)) == caretOffset;
    if (atLineStart && project != null) {
      int offset = editor.getCaretModel().getOffset();
      PsiDocumentManager.getInstance(project)
          .commitDocument(doc); // Sync document and PSI before formatting.
      String properIndent =
          offset >= doc.getTextLength()
              ? ""
              : CodeStyleFacade.getInstance(project).getLineIndent(doc, offset);
      if (properIndent != null) {
        int tabSize = editor.getSettings().getTabSize(project);
        for (int i = 0; i < properIndent.length(); i++) {
          if (properIndent.charAt(i) == ' ') {
            afterLineEnd--;
          } else if (properIndent.charAt(i) == '\t') {
            if (afterLineEnd < tabSize) {
              break;
            }
            afterLineEnd -= tabSize;
          }
          buf.append(properIndent.charAt(i));
          if (afterLineEnd == 0) break;
        }
      }
    }

    for (int i = 0; i < afterLineEnd; i++) {
      buf.append(' ');
    }

    return buf.toString();
  }
 private static Pair<Integer /* start offset to use */, Integer /* indent symbols to strip */>
     calcIndentSymbolsToStrip(@NotNull Document document, int startOffset, int endOffset) {
   int startLine = document.getLineNumber(startOffset);
   int endLine = document.getLineNumber(endOffset);
   CharSequence text = document.getCharsSequence();
   int maximumCommonIndent = Integer.MAX_VALUE;
   int firstLineStart = startOffset;
   int firstLineEnd = startOffset;
   for (int line = startLine; line <= endLine; line++) {
     int lineStartOffset = document.getLineStartOffset(line);
     int lineEndOffset = document.getLineEndOffset(line);
     if (line == startLine) {
       firstLineStart = lineStartOffset;
       firstLineEnd = lineEndOffset;
     }
     int nonWsOffset = lineEndOffset;
     for (int i = lineStartOffset;
         i < lineEndOffset && (i - lineStartOffset) < maximumCommonIndent && i < endOffset;
         i++) {
       char c = text.charAt(i);
       if (c != ' ' && c != '\t') {
         nonWsOffset = i;
         break;
       }
     }
     if (nonWsOffset >= lineEndOffset) {
       continue; // Blank line
     }
     int indent = nonWsOffset - lineStartOffset;
     maximumCommonIndent = Math.min(maximumCommonIndent, indent);
     if (maximumCommonIndent == 0) {
       break;
     }
   }
   int startOffsetToUse =
       Math.min(firstLineEnd, Math.max(startOffset, firstLineStart + maximumCommonIndent));
   return Pair.create(startOffsetToUse, maximumCommonIndent);
 }
Example #23
0
        @Override
        @SuppressWarnings({"AssignmentToForLoopParameter"})
        public void paint(
            @NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) {
          int startOffset = highlighter.getStartOffset();
          final Document doc = highlighter.getDocument();
          if (startOffset >= doc.getTextLength()) return;

          final int endOffset = highlighter.getEndOffset();
          final int endLine = doc.getLineNumber(endOffset);

          int off;
          int startLine = doc.getLineNumber(startOffset);
          IndentGuideDescriptor descriptor =
              editor.getIndentsModel().getDescriptor(startLine, endLine);

          final CharSequence chars = doc.getCharsSequence();
          do {
            int start = doc.getLineStartOffset(startLine);
            int end = doc.getLineEndOffset(startLine);
            off = CharArrayUtil.shiftForward(chars, start, end, " \t");
            startLine--;
          } while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n');

          final VisualPosition startPosition = editor.offsetToVisualPosition(off);
          int indentColumn = startPosition.column;

          // It's considered that indent guide can cross not only white space but comments, javadocs
          // etc. Hence, there is a possible
          // case that the first indent guide line is, say, single-line comment where comment
          // symbols ('//') are located at the first
          // visual column. We need to calculate correct indent guide column then.
          int lineShift = 1;
          if (indentColumn <= 0 && descriptor != null) {
            indentColumn = descriptor.indentLevel;
            lineShift = 0;
          }
          if (indentColumn <= 0) return;

          final FoldingModel foldingModel = editor.getFoldingModel();
          if (foldingModel.isOffsetCollapsed(off)) return;

          final FoldRegion headerRegion =
              foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off)));
          final FoldRegion tailRegion =
              foldingModel.getCollapsedRegionAtOffset(
                  doc.getLineStartOffset(doc.getLineNumber(endOffset)));

          if (tailRegion != null && tailRegion == headerRegion) return;

          final boolean selected;
          final IndentGuideDescriptor guide = editor.getIndentsModel().getCaretIndentGuide();
          if (guide != null) {
            final CaretModel caretModel = editor.getCaretModel();
            final int caretOffset = caretModel.getOffset();
            selected =
                caretOffset >= off
                    && caretOffset < endOffset
                    && caretModel.getLogicalPosition().column == indentColumn;
          } else {
            selected = false;
          }

          Point start =
              editor.visualPositionToXY(
                  new VisualPosition(startPosition.line + lineShift, indentColumn));
          final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset);
          Point end =
              editor.visualPositionToXY(new VisualPosition(endPosition.line, endPosition.column));
          int maxY = end.y;
          if (endPosition.line == editor.offsetToVisualPosition(doc.getTextLength()).line) {
            maxY += editor.getLineHeight();
          }

          Rectangle clip = g.getClipBounds();
          if (clip != null) {
            if (clip.y >= maxY || clip.y + clip.height <= start.y) {
              return;
            }
            maxY = Math.min(maxY, clip.y + clip.height);
          }

          final EditorColorsScheme scheme = editor.getColorsScheme();
          g.setColor(
              selected
                  ? scheme.getColor(EditorColors.SELECTED_INDENT_GUIDE_COLOR)
                  : scheme.getColor(EditorColors.INDENT_GUIDE_COLOR));

          // There is a possible case that indent line intersects soft wrap-introduced text.
          // Example:
          //     this is a long line <soft-wrap>
          // that| is soft-wrapped
          //     |
          //     | <- vertical indent
          //
          // Also it's possible that no additional intersections are added because of soft wrap:
          //     this is a long line <soft-wrap>
          //     |   that is soft-wrapped
          //     |
          //     | <- vertical indent
          // We want to use the following approach then:
          //     1. Show only active indent if it crosses soft wrap-introduced text;
          //     2. Show indent as is if it doesn't intersect with soft wrap-introduced text;
          if (selected) {
            g.drawLine(start.x + 2, start.y, start.x + 2, maxY);
          } else {
            int y = start.y;
            int newY = start.y;
            SoftWrapModel softWrapModel = editor.getSoftWrapModel();
            int lineHeight = editor.getLineHeight();
            for (int i = Math.max(0, startLine + lineShift); i < endLine && newY < maxY; i++) {
              List<? extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForLine(i);
              int logicalLineHeight = softWraps.size() * lineHeight;
              if (i > startLine + lineShift) {
                logicalLineHeight +=
                    lineHeight; // We assume that initial 'y' value points just below the target
                // line.
              }
              if (!softWraps.isEmpty() && softWraps.get(0).getIndentInColumns() < indentColumn) {
                if (y < newY
                    || i
                        > startLine
                            + lineShift) { // There is a possible case that soft wrap is located on
                  // indent start line.
                  g.drawLine(start.x + 2, y, start.x + 2, newY + lineHeight);
                }
                newY += logicalLineHeight;
                y = newY;
              } else {
                newY += logicalLineHeight;
              }

              FoldRegion foldRegion =
                  foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(i));
              if (foldRegion != null && foldRegion.getEndOffset() < doc.getTextLength()) {
                i = doc.getLineNumber(foldRegion.getEndOffset());
              }
            }

            if (y < maxY) {
              g.drawLine(start.x + 2, y, start.x + 2, maxY);
            }
          }
        }
    @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 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;
    }
Example #26
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);
      }
    }
  }
  private void paintBorderEffect(
      Graphics2D g,
      ClipDetector clipDetector,
      int startOffset,
      int endOffset,
      TextAttributes attributes) {
    if (!clipDetector.rangeCanBeVisible(startOffset, endOffset)) return;
    int startLine = myDocument.getLineNumber(startOffset);
    int endLine = myDocument.getLineNumber(endOffset);
    if (startLine + 1 == endLine
        && startOffset == myDocument.getLineStartOffset(startLine)
        && endOffset == myDocument.getLineStartOffset(endLine)) {
      // special case of line highlighters
      endLine--;
      endOffset = myDocument.getLineEndOffset(endLine);
    }

    boolean rounded = attributes.getEffectType() == EffectType.ROUNDED_BOX;
    int lineHeight = myView.getLineHeight() - 1;
    g.setColor(attributes.getEffectColor());
    VisualPosition startPosition = myView.offsetToVisualPosition(startOffset, true, false);
    VisualPosition endPosition = myView.offsetToVisualPosition(endOffset, false, true);
    if (startPosition.line == endPosition.line) {
      int y = myView.visualLineToY(startPosition.line);
      TFloatArrayList ranges = adjustedLogicalRangeToVisualRanges(startOffset, endOffset);
      for (int i = 0; i < ranges.size() - 1; i += 2) {
        int startX = (int) ranges.get(i);
        int endX = (int) ranges.get(i + 1);
        if (rounded) {
          UIUtil.drawRectPickedOut(g, startX, y, endX - startX, lineHeight);
        } else {
          g.drawRect(startX, y, endX - startX, lineHeight);
        }
      }
    } else {
      int maxWidth = myView.getMaxWidthInLineRange(startPosition.line, endPosition.line) - 1;
      TFloatArrayList leadingRanges =
          adjustedLogicalRangeToVisualRanges(
              startOffset,
              myView.visualPositionToOffset(
                  new VisualPosition(startPosition.line, Integer.MAX_VALUE, true)));
      TFloatArrayList trailingRanges =
          adjustedLogicalRangeToVisualRanges(
              myView.visualPositionToOffset(new VisualPosition(endPosition.line, 0)), endOffset);
      if (!leadingRanges.isEmpty() && !trailingRanges.isEmpty()) {
        boolean containsInnerLines = endPosition.line > startPosition.line + 1;
        int leadingTopY = myView.visualLineToY(startPosition.line);
        int leadingBottomY = leadingTopY + lineHeight;
        int trailingTopY = myView.visualLineToY(endPosition.line);
        int trailingBottomY = trailingTopY + lineHeight;
        float start = 0;
        float end = 0;
        float leftGap = leadingRanges.get(0) - (containsInnerLines ? 0 : trailingRanges.get(0));
        int adjustY =
            leftGap == 0 ? 2 : leftGap > 0 ? 1 : 0; // avoiding 1-pixel gap between aligned lines
        for (int i = 0; i < leadingRanges.size() - 1; i += 2) {
          start = leadingRanges.get(i);
          end = leadingRanges.get(i + 1);
          if (i > 0) {
            drawLine(g, leadingRanges.get(i - 1), leadingBottomY, start, leadingBottomY, rounded);
          }
          drawLine(g, start, leadingBottomY + (i == 0 ? adjustY : 0), start, leadingTopY, rounded);
          if ((i + 2) < leadingRanges.size()) {
            drawLine(g, start, leadingTopY, end, leadingTopY, rounded);
            drawLine(g, end, leadingTopY, end, leadingBottomY, rounded);
          }
        }
        end = Math.max(end, maxWidth);
        drawLine(g, start, leadingTopY, end, leadingTopY, rounded);
        drawLine(g, end, leadingTopY, end, trailingTopY - 1, rounded);
        float targetX = trailingRanges.get(trailingRanges.size() - 1);
        drawLine(g, end, trailingTopY - 1, targetX, trailingTopY - 1, rounded);
        adjustY =
            end == targetX
                ? -2
                : -1; // for lastX == targetX we need to avoid a gap when rounding is used
        for (int i = trailingRanges.size() - 2; i >= 0; i -= 2) {
          start = trailingRanges.get(i);
          end = trailingRanges.get(i + 1);

          drawLine(g, end, trailingTopY + (i == 0 ? adjustY : 0), end, trailingBottomY, rounded);
          drawLine(g, end, trailingBottomY, start, trailingBottomY, rounded);
          drawLine(g, start, trailingBottomY, start, trailingTopY, rounded);
          if (i > 0) {
            drawLine(g, start, trailingTopY, trailingRanges.get(i - 1), trailingTopY, rounded);
          }
        }
        float lastX = start;
        if (containsInnerLines) {
          if (start > 0) {
            drawLine(g, start, trailingTopY, start, trailingTopY - 1, rounded);
            drawLine(g, start, trailingTopY - 1, 0, trailingTopY - 1, rounded);
            drawLine(g, 0, trailingTopY - 1, 0, leadingBottomY + 1, rounded);
          } else {
            drawLine(g, start, trailingTopY, 0, leadingBottomY + 1, rounded);
          }
          lastX = 0;
        }
        targetX = leadingRanges.get(0);
        if (lastX < targetX) {
          drawLine(g, lastX, leadingBottomY + 1, targetX, leadingBottomY + 1, rounded);
        } else {
          drawLine(g, lastX, leadingBottomY + 1, lastX, leadingBottomY, rounded);
          drawLine(g, lastX, leadingBottomY, targetX, leadingBottomY, rounded);
        }
      }
    }
  }
  public void doWrapLongLinesIfNecessary(
      @NotNull final Editor editor,
      @NotNull final Project project,
      @NotNull Document document,
      int startOffset,
      int endOffset) {
    // Normalization.
    int startOffsetToUse = Math.min(document.getTextLength(), Math.max(0, startOffset));
    int endOffsetToUse = Math.min(document.getTextLength(), Math.max(0, endOffset));

    LineWrapPositionStrategy strategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(editor);
    CharSequence text = document.getCharsSequence();
    int startLine = document.getLineNumber(startOffsetToUse);
    int endLine = document.getLineNumber(Math.max(0, endOffsetToUse - 1));
    int maxLine = Math.min(document.getLineCount(), endLine + 1);
    int tabSize = EditorUtil.getTabSize(editor);
    if (tabSize <= 0) {
      tabSize = 1;
    }
    int spaceSize = EditorUtil.getSpaceWidth(Font.PLAIN, editor);
    int[] shifts = new int[2];
    // shifts[0] - lines shift.
    // shift[1] - offset shift.

    for (int line = startLine; line < maxLine; line++) {
      int startLineOffset = document.getLineStartOffset(line);
      int endLineOffset = document.getLineEndOffset(line);
      final int preferredWrapPosition =
          calculatePreferredWrapPosition(
              editor, text, tabSize, spaceSize, startLineOffset, endLineOffset, endOffsetToUse);

      if (preferredWrapPosition < 0 || preferredWrapPosition >= endLineOffset) {
        continue;
      }
      if (preferredWrapPosition >= endOffsetToUse) {
        return;
      }

      // We know that current line exceeds right margin if control flow reaches this place, so, wrap
      // it.
      int wrapOffset =
          strategy.calculateWrapPosition(
              document,
              editor.getProject(),
              Math.max(startLineOffset, startOffsetToUse),
              Math.min(endLineOffset, endOffsetToUse),
              preferredWrapPosition,
              false,
              false);
      if (wrapOffset < 0 // No appropriate wrap position is found.
          // No point in splitting line when its left part contains only white spaces, example:
          //    line start -> |                   | <- right margin
          //                  |   aaaaaaaaaaaaaaaa|aaaaaaaaaaaaaaaaaaaa() <- don't want to wrap this
          // line even if it exceeds right margin
          || CharArrayUtil.shiftBackward(text, startLineOffset, wrapOffset - 1, " \t")
              < startLineOffset) {
        continue;
      }

      // Move caret to the target position and emulate pressing <enter>.
      editor.getCaretModel().moveToOffset(wrapOffset);
      emulateEnter(editor, project, shifts);

      // We know that number of lines is just increased, hence, update the data accordingly.
      maxLine += shifts[0];
      endOffsetToUse += shifts[1];
    }
  }
  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);
  }
 public int getCaretOffset(Document document) {
   return document.getLineStartOffset(position.line) + position.column;
 }