Beispiel #1
0
 /** Patches attributes to be visible under debugger active line */
 @SuppressWarnings("UseJBColor")
 private static TextAttributes patchAttributesColor(
     TextAttributes attributes, TextRange range, Editor editor) {
   int line = editor.offsetToLogicalPosition(range.getStartOffset()).line;
   for (RangeHighlighter highlighter : editor.getMarkupModel().getAllHighlighters()) {
     if (!highlighter.isValid()) continue;
     if (highlighter.getTargetArea() == HighlighterTargetArea.LINES_IN_RANGE
         && editor.offsetToLogicalPosition(highlighter.getStartOffset()).line == line) {
       TextAttributes textAttributes = highlighter.getTextAttributes();
       if (textAttributes != null) {
         Color color = textAttributes.getBackgroundColor();
         if (color != null
             && color.getBlue() > 128
             && color.getRed() < 128
             && color.getGreen() < 128) {
           TextAttributes clone = attributes.clone();
           clone.setForegroundColor(Color.orange);
           clone.setEffectColor(Color.orange);
           return clone;
         }
       }
     }
   }
   return attributes;
 }
Beispiel #2
0
  public static void showInfoTooltip(
      @NotNull final HighlightInfo info,
      final Editor editor,
      final int defaultOffset,
      final int currentWidth) {
    if (info.toolTip == null || info.getSeverity() == HighlightSeverity.INFORMATION) return;
    Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
    int endOffset = info.highlighter.getEndOffset();
    int startOffset = info.highlighter.getStartOffset();

    Point top = editor.logicalPositionToXY(editor.offsetToLogicalPosition(startOffset));
    Point bottom = editor.logicalPositionToXY(editor.offsetToLogicalPosition(endOffset));

    Point bestPoint = new Point(top.x, bottom.y + editor.getLineHeight());

    if (!visibleArea.contains(bestPoint)) {
      bestPoint = editor.logicalPositionToXY(editor.offsetToLogicalPosition(defaultOffset));
    }

    Point p =
        SwingUtilities.convertPoint(
            editor.getContentComponent(),
            bestPoint,
            editor.getComponent().getRootPane().getLayeredPane());
    TooltipController.getInstance()
        .showTooltip(editor, p, info.toolTip, currentWidth, false, DAEMON_INFO_GROUP);
  }
 private static LineRange expandLineRangeToCoverPsiElements(
     final LineRange range, Editor editor, final PsiFile file) {
   Pair<PsiElement, PsiElement> psiRange = getElementRange(editor, file, range);
   if (psiRange == null) return null;
   final PsiElement parent =
       PsiTreeUtil.findCommonParent(psiRange.getFirst(), psiRange.getSecond());
   Pair<PsiElement, PsiElement> elementRange =
       getElementRange(parent, psiRange.getFirst(), psiRange.getSecond());
   if (elementRange == null) return null;
   int endOffset = elementRange.getSecond().getTextRange().getEndOffset();
   Document document = editor.getDocument();
   if (endOffset > document.getTextLength()) {
     LOG.assertTrue(!PsiDocumentManager.getInstance(file.getProject()).isUncommited(document));
     LOG.assertTrue(PsiDocumentManagerImpl.checkConsistency(file, document));
   }
   int endLine;
   if (endOffset == document.getTextLength()) {
     endLine = document.getLineCount();
   } else {
     endLine = editor.offsetToLogicalPosition(endOffset).line + 1;
     endLine = Math.min(endLine, document.getLineCount());
   }
   int startLine =
       Math.min(
           range.startLine,
           editor.offsetToLogicalPosition(elementRange.getFirst().getTextOffset()).line);
   endLine = Math.max(endLine, range.endLine);
   return new LineRange(startLine, endLine);
 }
  public void commentRange(
      int startOffset,
      int endOffset,
      String commentPrefix,
      String commentSuffix,
      Commenter commenter) {
    final CharSequence chars = myDocument.getCharsSequence();
    LogicalPosition caretPosition = myCaret.getLogicalPosition();

    if (startOffset == 0 || chars.charAt(startOffset - 1) == '\n') {
      if (endOffset == myDocument.getTextLength()
          || endOffset > 0 && chars.charAt(endOffset - 1) == '\n') {
        CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(myProject);
        CommonCodeStyleSettings settings =
            CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(myFile.getLanguage());
        String space;
        if (!settings.BLOCK_COMMENT_AT_FIRST_COLUMN) {
          final FileType fileType = myFile.getFileType();
          int line1 = myEditor.offsetToLogicalPosition(startOffset).line;
          int line2 = myEditor.offsetToLogicalPosition(endOffset - 1).line;
          Indent minIndent =
              CommentUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType);
          if (minIndent == null) {
            minIndent = codeStyleManager.zeroIndent();
          }
          space = codeStyleManager.fillIndent(minIndent, fileType);
        } else {
          space = "";
        }
        final StringBuilder nestingPrefix = new StringBuilder(space).append(commentPrefix);
        if (!commentPrefix.endsWith("\n")) {
          nestingPrefix.append("\n");
        }
        final StringBuilder nestingSuffix = new StringBuilder(space);
        nestingSuffix.append(
            commentSuffix.startsWith("\n") ? commentSuffix.substring(1) : commentSuffix);
        nestingSuffix.append("\n");
        TextRange range =
            insertNestedComments(
                startOffset,
                endOffset,
                nestingPrefix.toString(),
                nestingSuffix.toString(),
                commenter);
        myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
        LogicalPosition pos = new LogicalPosition(caretPosition.line + 1, caretPosition.column);
        myCaret.moveToLogicalPosition(pos);
        return;
      }
    }

    TextRange range =
        insertNestedComments(startOffset, endOffset, commentPrefix, commentSuffix, commenter);
    myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
    LogicalPosition pos =
        new LogicalPosition(caretPosition.line, caretPosition.column + commentPrefix.length());
    myCaret.moveToLogicalPosition(pos);
  }
Beispiel #5
0
  @Override
  protected LineRange getElementSourceLineRange(
      @NotNull PsiElement element, @NotNull Editor editor, @NotNull LineRange oldRange) {
    TextRange textRange = element.getTextRange();
    if (editor.getDocument().getTextLength() < textRange.getEndOffset()) return null;

    int startLine = editor.offsetToLogicalPosition(textRange.getStartOffset()).line;
    int endLine = editor.offsetToLogicalPosition(textRange.getEndOffset()).line + 1;

    return new LineRange(startLine, endLine);
  }
  private static void restoreBlockSelection(
      Editor editor, List<RangeMarker> caretsAfter, int caretLine) {
    int column = -1;
    int minLine = Integer.MAX_VALUE;
    int maxLine = -1;
    for (RangeMarker marker : caretsAfter) {
      if (marker.isValid()) {
        LogicalPosition lp = editor.offsetToLogicalPosition(marker.getStartOffset());
        if (column == -1) {
          column = lp.column;
        } else if (column != lp.column) {
          return;
        }
        minLine = Math.min(minLine, lp.line);
        maxLine = Math.max(maxLine, lp.line);

        if (lp.line == caretLine) {
          editor.getCaretModel().moveToLogicalPosition(lp);
        }
      }
    }
    editor
        .getSelectionModel()
        .setBlockSelection(
            new LogicalPosition(minLine, column), new LogicalPosition(maxLine, column));
  }
  public void testColumnModeBlockSelectionWithGaps() {
    String text =
        "public class TestClass {\n"
            + "\n"
            + "    int field;\n"
            + "\n"
            + "    int otherField;\n"
            + "}";
    init(text);

    int blockSelectionStartOffset = text.indexOf("int");
    Editor editor = myFixture.getEditor();
    ((EditorEx) editor).setColumnMode(true);
    LogicalPosition blockSelectionStartPosition =
        editor.offsetToLogicalPosition(blockSelectionStartOffset);
    LogicalPosition blockSelectionEndPosition =
        new LogicalPosition(
            blockSelectionStartPosition.line + 2, blockSelectionStartPosition.column + 16);
    editor
        .getSelectionModel()
        .setBlockSelection(blockSelectionStartPosition, blockSelectionEndPosition);

    verifySyntaxInfo(
        "foreground=java.awt.Color[r=0,g=0,b=128],fontStyle=1,text=int \n"
            + "foreground=java.awt.Color[r=102,g=14,b=122],text=field\n"
            + "foreground=java.awt.Color[r=0,g=0,b=0],fontStyle=0,text=;\n"
            + "text=\n"
            + "\n"
            + "text=\n"
            + "\n"
            + "foreground=java.awt.Color[r=0,g=0,b=128],fontStyle=1,text=int \n"
            + "foreground=java.awt.Color[r=102,g=14,b=122],text=otherField\n"
            + "foreground=java.awt.Color[r=0,g=0,b=0],fontStyle=0,text=;\n");
  }
  private void updateCursorHighlighting(boolean scroll) {
    hideBalloon();

    if (myCursorHighlighter != null) {
      HighlightManager.getInstance(mySearchResults.getProject())
          .removeSegmentHighlighter(mySearchResults.getEditor(), myCursorHighlighter);
      myCursorHighlighter = null;
    }

    final FindResult cursor = mySearchResults.getCursor();
    Editor editor = mySearchResults.getEditor();
    SelectionModel selection = editor.getSelectionModel();
    if (cursor != null) {
      Set<RangeHighlighter> dummy = new HashSet<RangeHighlighter>();
      highlightRange(
          cursor, new TextAttributes(null, null, Color.BLACK, EffectType.ROUNDED_BOX, 0), dummy);
      if (!dummy.isEmpty()) {
        myCursorHighlighter = dummy.iterator().next();
      }

      if (scroll) {
        if (mySearchResults.getFindModel().isGlobal()) {
          FoldingModel foldingModel = editor.getFoldingModel();
          final FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions();

          foldingModel.runBatchFoldingOperation(
              new Runnable() {
                @Override
                public void run() {
                  for (FoldRegion region : allRegions) {
                    if (!region.isValid()) continue;
                    if (cursor.intersects(TextRange.create(region))) {
                      region.setExpanded(true);
                    }
                  }
                }
              });
          selection.setSelection(cursor.getStartOffset(), cursor.getEndOffset());

          editor.getCaretModel().moveToOffset(cursor.getEndOffset());
          editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
        } else {
          if (!SearchResults.insideVisibleArea(editor, cursor)) {
            LogicalPosition pos = editor.offsetToLogicalPosition(cursor.getStartOffset());
            editor.getScrollingModel().scrollTo(pos, ScrollType.CENTER);
          }
        }
      }
      editor
          .getScrollingModel()
          .runActionOnScrollingFinished(
              new Runnable() {
                @Override
                public void run() {
                  showReplacementPreview();
                }
              });
    }
  }
  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)));
  }
  private static boolean isDeclarationVisible(PsiElement container, Editor editor) {
    Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
    final TextRange range = DeclarationRangeUtil.getPossibleDeclarationAtRange(container);
    if (range == null) {
      return false;
    }

    LogicalPosition pos = editor.offsetToLogicalPosition(range.getStartOffset());
    Point loc = editor.logicalPositionToXY(pos);
    return loc.y >= viewRect.y;
  }
 private static void highlightInEditor(
     final Project project, final IncludeDuplicate pair, final Editor editor) {
   final HighlightManager highlightManager = HighlightManager.getInstance(project);
   EditorColorsManager colorsManager = EditorColorsManager.getInstance();
   TextAttributes attributes =
       colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
   final int startOffset = pair.getStart().getTextRange().getStartOffset();
   final int endOffset = pair.getEnd().getTextRange().getEndOffset();
   highlightManager.addRangeHighlight(editor, startOffset, endOffset, attributes, true, null);
   final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(startOffset);
   editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.MAKE_VISIBLE);
 }
  public void testBlockSelection() {
    String text =
        "package org;\n"
            + "\n"
            + "public class TestClass {\n"
            + "\n"
            + "    int field;\n"
            + "\n"
            + "    public int getField() {\n"
            + "        return field;\n"
            + "    }\n"
            + "}";
    init(text);

    int blockSelectionStartOffset = text.indexOf("public int");
    Editor editor = myFixture.getEditor();
    LogicalPosition blockSelectionStartPosition =
        editor.offsetToLogicalPosition(blockSelectionStartOffset);
    LogicalPosition blockSelectionEndPosition =
        new LogicalPosition(
            blockSelectionStartPosition.line + 2,
            editor.offsetToLogicalPosition(text.indexOf('{', blockSelectionStartOffset)).column
                + 1);
    editor
        .getSelectionModel()
        .setBlockSelection(blockSelectionStartPosition, blockSelectionEndPosition);

    verifySyntaxInfo(
        "foreground=java.awt.Color[r=0,g=0,b=128],fontStyle=1,text=public int \n"
            + "foreground=java.awt.Color[r=0,g=0,b=0],fontStyle=0,text=getField() {\n"
            + "text=\n"
            + "\n"
            + "text=    \n"
            + "foreground=java.awt.Color[r=0,g=0,b=128],fontStyle=1,text=return \n"
            + "foreground=java.awt.Color[r=102,g=14,b=122],text=field\n"
            + "foreground=java.awt.Color[r=0,g=0,b=0],fontStyle=0,text=;\n"
            + "text=\n"
            + "\n"
            + "text=}\n");
  }
 /**
  * Applies given caret/selection state to the editor. Editor text must have been set up
  * previously.
  */
 public static void setCaretsAndSelection(Editor editor, CaretAndSelectionState caretsState) {
   CaretModel caretModel = editor.getCaretModel();
   if (caretModel.supportsMultipleCarets()) {
     List<CaretState> states = new ArrayList<CaretState>(caretsState.carets.size());
     for (CaretInfo caret : caretsState.carets) {
       states.add(
           new CaretState(
               caret.position == null
                   ? null
                   : editor.offsetToLogicalPosition(caret.getCaretOffset(editor.getDocument())),
               caret.selection == null
                   ? null
                   : editor.offsetToLogicalPosition(caret.selection.getStartOffset()),
               caret.selection == null
                   ? null
                   : editor.offsetToLogicalPosition(caret.selection.getEndOffset())));
     }
     caretModel.setCaretsAndSelections(states);
   } else {
     assertEquals("Multiple carets are not supported by the model", 1, caretsState.carets.size());
     CaretInfo caret = caretsState.carets.get(0);
     if (caret.position != null) {
       caretModel.moveToOffset(caret.getCaretOffset(editor.getDocument()));
     }
     if (caret.selection != null) {
       editor
           .getSelectionModel()
           .setSelection(caret.selection.getStartOffset(), caret.selection.getEndOffset());
     } else {
       editor.getSelectionModel().removeSelection();
     }
   }
   if (caretsState.blockSelection != null) {
     editor
         .getSelectionModel()
         .setBlockSelection(
             editor.offsetToLogicalPosition(caretsState.blockSelection.getStartOffset()),
             editor.offsetToLogicalPosition(caretsState.blockSelection.getEndOffset()));
   }
 }
  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);
    }
    /* } */
  }
Beispiel #15
0
  @Override
  public void paint(Graphics gfx) {
    Graphics2D g = (Graphics2D) gfx;
    g.setColor(editor.getColorsScheme().getDefaultBackground());
    g.fillRect(0, 0, getWidth(), getHeight());

    logger.debug(String.format("Rendering to buffer: %d", activeBuffer));
    if (activeBuffer >= 0) {
      paintSelection(g);

      Minimap minimap = minimaps[activeBuffer];

      Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();

      double documentEndY =
          editor
              .logicalPositionToXY(
                  editor.offsetToLogicalPosition(editor.getDocument().getTextLength() - 1))
              .getY();

      coords
          .setMinimap(minimap)
          .setPanelHeight(getHeight())
          .setPanelWidth(getWidth())
          .setPercentageComplete(
              visibleArea.getMinY()
                  / (documentEndY - (visibleArea.getMaxY() - visibleArea.getMinY())))
          .setHidpiScale(getHidpiScale());

      Rectangle src = coords.getImageSource();
      Rectangle dest = coords.getImageDestination();

      // Draw the image and scale it to stretch vertically.
      g.drawImage(
          minimap.img, // source image
          dest.x,
          dest.y,
          dest.width,
          dest.height,
          src.x,
          src.y,
          src.width,
          src.height,
          null);

      paintVisibleWindow(g);
    }
  }
Beispiel #16
0
  public boolean checkAvailable(
      @NotNull final Editor editor,
      @NotNull final PsiFile file,
      @NotNull final MoveInfo info,
      final boolean down) {
    LineRange range = StatementUpDownMover.getLineRangeFromSelection(editor);

    final int maxLine = editor.offsetToLogicalPosition(editor.getDocument().getTextLength()).line;
    if (range.startLine == 0 && !down) return false;
    if (range.endLine >= maxLine && down) return false;

    int nearLine = down ? range.endLine : range.startLine - 1;
    info.toMove = range;
    info.toMove2 = new LineRange(nearLine, nearLine + 1);

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

    return range.endLine <= maxLine || !isDown;
  }
Beispiel #18
0
    public void documentChanged(@NotNull DocumentEvent event) {
      if (!VimPlugin.isEnabled()) {
        return;
      }

      Project[] projs = ProjectManager.getInstance().getOpenProjects();
      for (Project proj : projs) {
        Editor[] editors = EditorFactory.getInstance().getEditors(event.getDocument(), proj);
        for (Editor editor : editors) {
          Collection hls = EditorData.getLastHighlights(editor);
          if (hls == null) {
            continue;
          }

          int soff = event.getOffset();
          int eoff = soff + event.getNewLength();

          if (logger.isDebugEnabled()) {
            logger.debug("hls=" + hls);
            logger.debug("event=" + event);
          }
          Iterator iter = hls.iterator();
          while (iter.hasNext()) {
            RangeHighlighter rh = (RangeHighlighter) iter.next();
            if (!rh.isValid() || (eoff >= rh.getStartOffset() && soff <= rh.getEndOffset())) {
              iter.remove();
              editor.getMarkupModel().removeHighlighter(rh);
            }
          }

          int sl = editor.offsetToLogicalPosition(soff).line;
          int el = editor.offsetToLogicalPosition(eoff).line;
          VimPlugin.getSearch().highlightSearchLines(editor, false, sl, el);
          hls = EditorData.getLastHighlights(editor);
          if (logger.isDebugEnabled()) {
            logger.debug("sl=" + sl + ", el=" + el);
            logger.debug("hls=" + hls);
          }
        }
      }
    }
    @Override
    @NotNull
    public Pair<Point, Short> getBestPointPosition(
        LightweightHint hint,
        final PsiElement list,
        int offset,
        final boolean awtTooltip,
        short preferredPosition) {
      if (list != null) {
        TextRange range = list.getTextRange();
        if (!range.contains(offset)) {
          offset = range.getStartOffset() + 1;
        }
      }
      if (previousOffset == offset) return Pair.create(previousBestPoint, previousBestPosition);

      final boolean isMultiline =
          list != null && StringUtil.containsAnyChar(list.getText(), "\n\r");
      final LogicalPosition pos = myEditor.offsetToLogicalPosition(offset);
      Pair<Point, Short> position;

      if (!isMultiline) {
        position =
            chooseBestHintPosition(
                myEditor.getProject(),
                myEditor,
                pos.line,
                pos.column,
                hint,
                awtTooltip,
                preferredPosition);
      } else {
        Point p = HintManagerImpl.getHintPosition(hint, myEditor, pos, HintManager.ABOVE);
        position = new Pair<Point, Short>(p, HintManager.ABOVE);
      }
      previousBestPoint = position.getFirst();
      previousBestPosition = position.getSecond();
      previousOffset = offset;
      return position;
    }
  private void highlightUsages() {
    Editor e = getEditor();
    if (e == null) return;
    boolean foundVisibleElement = false;
    int offset = Integer.MAX_VALUE;
    for (SearchResult sr : searchResults.values()) {
      Map<Node, PsiElement> match = sr.getMatch();
      TextAttributes textAttributes = createUniqueTextAttrsFor(sr);
      PsiElement targetElement = match.get(pattern.getTheOne());

      if (!foundVisibleElement && insideVisibleArea(e, targetElement)) {
        foundVisibleElement = true;
      }
      if (!foundVisibleElement && targetElement.getTextOffset() < offset) {
        offset = targetElement.getTextOffset();
      }
      for (PsiElement element : match.values()) {
        TextAttributes attributes = textAttributes;
        if (element == targetElement) {
          attributes = MAIN_TARGET_ATTRIBUTES;
        }
        if (element instanceof XmlTag) {
          element = element.getFirstChild().getNextSibling();
        }
        TextRange textRange = element.getTextRange();

        e.getMarkupModel()
            .addRangeHighlighter(
                textRange.getStartOffset(),
                textRange.getEndOffset(),
                HighlighterLayer.SELECTION + 1,
                attributes,
                HighlighterTargetArea.EXACT_RANGE);
      }
    }
    if (!foundVisibleElement && offset != Integer.MAX_VALUE) {
      e.getScrollingModel().scrollTo(e.offsetToLogicalPosition(offset), ScrollType.CENTER);
    }
  }
Beispiel #21
0
  public boolean searchAndReplace(
      @NotNull Editor editor, @NotNull LineRange range, @NotNull String excmd, String exarg) {
    boolean res = true;

    // Explicitly exit visual mode here, so that visual mode marks don't change when we move the
    // cursor to a match.
    if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
      VimPlugin.getMotion().exitVisual(editor);
    }

    CharPointer cmd = new CharPointer(new StringBuffer(exarg));
    // sub_nsubs = 0;
    // sub_nlines = 0;

    int which_pat;
    if (excmd.equals("~")) {
      which_pat = RE_LAST; /* use last used regexp */
    } else {
      which_pat = RE_SUBST; /* use last substitute regexp */
    }

    CharPointer pat;
    CharPointer sub;
    char delimiter;
    /* new pattern and substitution */
    if (excmd.charAt(0) == 's'
        && !cmd.isNul()
        && !Character.isWhitespace(cmd.charAt())
        && "0123456789cegriIp|\"".indexOf(cmd.charAt()) == -1) {
      /* don't accept alphanumeric for separator */
      if (CharacterClasses.isAlpha(cmd.charAt())) {
        VimPlugin.showMessage(MessageHelper.message(Msg.E146));
        return false;
      }
      /*
       * undocumented vi feature:
       *  "\/sub/" and "\?sub?" use last used search pattern (almost like
       *  //sub/r).  "\&sub&" use last substitute pattern (like //sub/).
       */
      if (cmd.charAt() == '\\') {
        cmd.inc();
        if ("/?&".indexOf(cmd.charAt()) == -1) {
          VimPlugin.showMessage(MessageHelper.message(Msg.e_backslash));
          return false;
        }
        if (cmd.charAt() != '&') {
          which_pat = RE_SEARCH; /* use last '/' pattern */
        }
        pat = new CharPointer(""); /* empty search pattern */
        delimiter = cmd.charAt(); /* remember delimiter character */
        cmd.inc();
      } else /* find the end of the regexp */ {
        which_pat = RE_LAST; /* use last used regexp */
        delimiter = cmd.charAt(); /* remember delimiter character */
        cmd.inc();
        pat = cmd.ref(0); /* remember start of search pat */
        cmd = RegExp.skip_regexp(cmd, delimiter, true);
        if (cmd.charAt() == delimiter) /* end delimiter found */ {
          cmd.set('\u0000').inc(); /* replace it with a NUL */
        }
      }

      /*
       * Small incompatibility: vi sees '\n' as end of the command, but in
       * Vim we want to use '\n' to find/substitute a NUL.
       */
      sub = cmd.ref(0); /* remember the start of the substitution */

      while (!cmd.isNul()) {
        if (cmd.charAt() == delimiter) /* end delimiter found */ {
          cmd.set('\u0000').inc(); /* replace it with a NUL */
          break;
        }
        if (cmd.charAt(0) == '\\' && cmd.charAt(1) != 0) /* skip escaped characters */ {
          cmd.inc();
        }
        cmd.inc();
      }
    } else /* use previous pattern and substitution */ {
      if (lastReplace == null) /* there is no previous command */ {
        VimPlugin.showMessage(MessageHelper.message(Msg.e_nopresub));
        return false;
      }
      pat = null; /* search_regcomp() will use previous pattern */
      sub = new CharPointer(lastReplace);
    }

    /*
     * Find trailing options.  When '&' is used, keep old options.
     */
    if (cmd.charAt() == '&') {
      cmd.inc();
    } else {
      do_all = Options.getInstance().isSet("gdefault");
      do_ask = false;
      do_error = true;
      // do_print = false;
      do_ic = 0;
    }
    while (!cmd.isNul()) {
      /*
       * Note that 'g' and 'c' are always inverted, also when p_ed is off.
       * 'r' is never inverted.
       */
      if (cmd.charAt() == 'g') {
        do_all = !do_all;
      } else if (cmd.charAt() == 'c') {
        do_ask = !do_ask;
      } else if (cmd.charAt() == 'e') {
        do_error = !do_error;
      } else if (cmd.charAt() == 'r') /* use last used regexp */ {
        which_pat = RE_LAST;
      } else if (cmd.charAt() == 'i') /* ignore case */ {
        do_ic = 'i';
      } else if (cmd.charAt() == 'I') /* don't ignore case */ {
        do_ic = 'I';
      } else if (cmd.charAt() != 'p') {
        break;
      }
      cmd.inc();
    }

    int line1 = range.getStartLine();
    int line2 = range.getEndLine();

    /*
     * check for a trailing count
     */
    cmd = CharHelper.skipwhite(cmd);
    if (CharacterClasses.isDigit(cmd.charAt())) {
      int i = CharHelper.getdigits(cmd);
      if (i <= 0 && do_error) {
        VimPlugin.showMessage(MessageHelper.message(Msg.e_zerocount));
        return false;
      }
      line1 = line2;
      line2 = EditorHelper.normalizeLine(editor, line1 + i - 1);
    }

    /*
     * check for trailing command or garbage
     */
    cmd = CharHelper.skipwhite(cmd);
    if (!cmd.isNul() && cmd.charAt() != '"') /* if not end-of-line or comment */ {
      VimPlugin.showMessage(MessageHelper.message(Msg.e_trailing));
      return false;
    }

    String pattern = "";
    if (pat == null || pat.isNul()) {
      switch (which_pat) {
        case RE_LAST:
          pattern = lastPattern;
          break;
        case RE_SEARCH:
          pattern = lastSearch;
          break;
        case RE_SUBST:
          pattern = lastSubstitute;
          break;
      }
    } else {
      pattern = pat.toString();
    }

    lastSubstitute = pattern;
    if (pattern != null) {
      setLastPattern(editor, pattern);
    }

    // int start = editor.logicalPositionToOffset(new LogicalPosition(line1, 0));
    // int end = editor.logicalPositionToOffset(new LogicalPosition(line2,
    // EditorHelper.getLineLength(editor, line2)));
    int start = editor.getDocument().getLineStartOffset(line1);
    int end = editor.getDocument().getLineEndOffset(line2);

    RegExp sp;
    RegExp.regmmatch_T regmatch = new RegExp.regmmatch_T();
    sp = new RegExp();
    regmatch.regprog = sp.vim_regcomp(pattern, 1);
    if (regmatch.regprog == null) {
      if (do_error) {
        VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd));
      }
      return false;
    }

    /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
    if (do_ic == 'i') {
      regmatch.rmm_ic = true;
    } else if (do_ic == 'I') {
      regmatch.rmm_ic = false;
    }

    /*
     * ~ in the substitute pattern is replaced with the old pattern.
     * We do it here once to avoid it to be replaced over and over again.
     * But don't do it when it starts with "\=", then it's an expression.
     */
    if (!(sub.charAt(0) == '\\' && sub.charAt(1) == '=') && lastReplace != null) {
      StringBuffer tmp = new StringBuffer(sub.toString());
      int pos = 0;
      while ((pos = tmp.indexOf("~", pos)) != -1) {
        if (pos == 0 || tmp.charAt(pos - 1) != '\\') {
          tmp.replace(pos, pos + 1, lastReplace);
          pos += lastReplace.length();
        }
        pos++;
      }
      sub = new CharPointer(tmp);
    }

    lastReplace = sub.toString();

    searchHighlight(false);

    if (logger.isDebugEnabled()) {
      logger.debug("search range=[" + start + "," + end + "]");
      logger.debug("pattern=" + pattern + ", replace=" + sub);
    }
    int lastMatch = -1;
    int lastLine = -1;
    int searchcol = 0;
    boolean firstMatch = true;
    boolean got_quit = false;
    int lcount = EditorHelper.getLineCount(editor);
    for (int lnum = line1; lnum <= line2 && !got_quit; ) {
      CharacterPosition newpos = null;
      int nmatch = sp.vim_regexec_multi(regmatch, editor, lcount, lnum, searchcol);
      if (nmatch > 0) {
        if (firstMatch) {
          VimPlugin.getMark().saveJumpLocation(editor);
          firstMatch = false;
        }

        String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false);
        // logger.debug("found match[" + spos + "," + epos + "] - replace " + match);

        int line = lnum + regmatch.startpos[0].lnum;
        CharacterPosition startpos =
            new CharacterPosition(lnum + regmatch.startpos[0].lnum, regmatch.startpos[0].col);
        CharacterPosition endpos =
            new CharacterPosition(lnum + regmatch.endpos[0].lnum, regmatch.endpos[0].col);
        int startoff = EditorHelper.characterPositionToOffset(editor, startpos);
        int endoff = EditorHelper.characterPositionToOffset(editor, endpos);
        int newend = startoff + match.length();

        if (do_all || line != lastLine) {
          boolean doReplace = true;
          if (do_ask) {
            // editor.getSelectionModel().setSelection(startoff, endoff);
            RangeHighlighter hl = highlightConfirm(editor, startoff, endoff);
            int choice = getConfirmChoice(match);
            // editor.getSelectionModel().removeSelection();
            editor.getMarkupModel().removeHighlighter(hl);
            switch (choice) {
              case 0: // Yes
                doReplace = true;
                break;
              case 1: // No
                doReplace = false;
                break;
              case 2: // All
                do_ask = false;
                break;
              case JOptionPane.CLOSED_OPTION:
              case 3: // Quit
                doReplace = false;
                got_quit = true;
                break;
              case 4: // Last
                do_all = false;
                line2 = lnum;
                doReplace = true;
                break;
            }
          }

          if (doReplace) {
            editor.getDocument().replaceString(startoff, endoff, match);
            lastMatch = startoff;
            newpos = EditorHelper.offsetToCharacterPosition(editor, newend);

            lnum += newpos.line - endpos.line;
            line2 += newpos.line - endpos.line;
          }
        }

        lastLine = line;

        lnum += nmatch - 1;
        if (do_all && startoff != endoff) {
          if (newpos != null) {
            lnum = newpos.line;
            searchcol = newpos.column;
          } else {
            searchcol = endpos.column;
          }
        } else {
          searchcol = 0;
          lnum++;
        }
      } else {
        lnum++;
        searchcol = 0;
      }
    }

    if (lastMatch != -1) {
      MotionGroup.moveCaret(
          editor,
          VimPlugin.getMotion()
              .moveCaretToLineStartSkipLeading(
                  editor, editor.offsetToLogicalPosition(lastMatch).line));
    } else {
      VimPlugin.showMessage(MessageHelper.message(Msg.e_patnotf2, pattern));
    }

    return res;
  }
Beispiel #22
0
  private int findItOffset(
      @NotNull Editor editor, int startOffset, int count, int dir, boolean noSmartCase) {
    boolean wrap = Options.getInstance().isSet("wrapscan");
    TextRange range = findIt(editor, startOffset, count, dir, noSmartCase, wrap, true, true);
    if (range == null) {
      return -1;
    }

    // highlightMatch(editor, range.getStartOffset(), range.getEndOffset());

    ParsePosition pp = new ParsePosition(0);
    int res = range.getStartOffset();

    if (lastOffset == null) {
      return -1;
    }

    if (lastOffset.length() == 0) {
      return range.getStartOffset();
    } else if (Character.isDigit(lastOffset.charAt(0))
        || lastOffset.charAt(0) == '+'
        || lastOffset.charAt(0) == '-') {
      int lineOffset = 0;
      if (lastOffset.equals("+")) {
        lineOffset = 1;
      } else if (lastOffset.equals("-")) {
        lineOffset = -1;
      } else {
        if (lastOffset.charAt(0) == '+') {
          lastOffset = lastOffset.substring(1);
        }
        NumberFormat nf = NumberFormat.getIntegerInstance();
        pp = new ParsePosition(0);
        Number num = nf.parse(lastOffset, pp);
        if (num != null) {
          lineOffset = num.intValue();
        }
      }

      int line = editor.offsetToLogicalPosition(range.getStartOffset()).line;
      int newLine = EditorHelper.normalizeLine(editor, line + lineOffset);

      res = VimPlugin.getMotion().moveCaretToLineStart(editor, newLine);
    } else if ("ebs".indexOf(lastOffset.charAt(0)) != -1) {
      int charOffset = 0;
      if (lastOffset.length() >= 2) {
        if ("+-".indexOf(lastOffset.charAt(1)) != -1) {
          charOffset = 1;
        }
        NumberFormat nf = NumberFormat.getIntegerInstance();
        pp = new ParsePosition(lastOffset.charAt(1) == '+' ? 2 : 1);
        Number num = nf.parse(lastOffset, pp);
        if (num != null) {
          charOffset = num.intValue();
        }
      }

      int base = range.getStartOffset();
      if (lastOffset.charAt(0) == 'e') {
        base = range.getEndOffset() - 1;
      }

      res = Math.max(0, Math.min(base + charOffset, EditorHelper.getFileSize(editor) - 1));
    }

    int ppos = pp.getIndex();
    if (ppos < lastOffset.length() - 1 && lastOffset.charAt(ppos) == ';') {
      int flags;
      if (lastOffset.charAt(ppos + 1) == '/') {
        flags = Command.FLAG_SEARCH_FWD;
      } else if (lastOffset.charAt(ppos + 1) == '?') {
        flags = Command.FLAG_SEARCH_REV;
      } else {
        return res;
      }

      if (lastOffset.length() - ppos > 2) {
        ppos++;
      }

      res = search(editor, lastOffset.substring(ppos + 1), res, 1, flags);

      return res;
    } else {
      return res;
    }
  }
Beispiel #23
0
  private static void doPaste(
      final Editor editor,
      final Project project,
      final PsiFile file,
      final Document document,
      final Producer<Transferable> producer) {
    Transferable content = null;

    if (producer != null) {
      content = producer.produce();
    } else {
      CopyPasteManager manager = CopyPasteManager.getInstance();
      if (manager.areDataFlavorsAvailable(DataFlavor.stringFlavor)) {
        content = manager.getContents();
        if (content != null) {
          manager.stopKillRings();
        }
      }
    }

    if (content != null) {
      String text = null;
      try {
        text = (String) content.getTransferData(DataFlavor.stringFlavor);
      } catch (Exception e) {
        editor.getComponent().getToolkit().beep();
      }
      if (text == null) return;

      final CodeInsightSettings settings = CodeInsightSettings.getInstance();

      final Map<CopyPastePostProcessor, TextBlockTransferableData> extraData =
          new HashMap<CopyPastePostProcessor, TextBlockTransferableData>();
      for (CopyPastePostProcessor processor :
          Extensions.getExtensions(CopyPastePostProcessor.EP_NAME)) {
        TextBlockTransferableData data = processor.extractTransferableData(content);
        if (data != null) {
          extraData.put(processor, data);
        }
      }

      text = TextBlockTransferable.convertLineSeparators(text, "\n", extraData.values());

      final CaretModel caretModel = editor.getCaretModel();
      final SelectionModel selectionModel = editor.getSelectionModel();
      final int col = caretModel.getLogicalPosition().column;

      // There is a possible case that we want to perform paste while there is an active selection
      // at the editor and caret is located
      // inside it (e.g. Ctrl+A is pressed while caret is not at the zero column). We want to insert
      // the text at selection start column
      // then, hence, inserted block of text should be indented according to the selection start as
      // well.
      final int blockIndentAnchorColumn;
      final int caretOffset = caretModel.getOffset();
      if (selectionModel.hasSelection() && caretOffset >= selectionModel.getSelectionStart()) {
        blockIndentAnchorColumn =
            editor.offsetToLogicalPosition(selectionModel.getSelectionStart()).column;
      } else {
        blockIndentAnchorColumn = col;
      }

      // We assume that EditorModificationUtil.insertStringAtCaret() is smart enough to remove
      // currently selected text (if any).

      RawText rawText = RawText.fromTransferable(content);
      String newText = text;
      for (CopyPastePreProcessor preProcessor :
          Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) {
        newText = preProcessor.preprocessOnPaste(project, file, editor, newText, rawText);
      }
      int indentOptions =
          text.equals(newText) ? settings.REFORMAT_ON_PASTE : CodeInsightSettings.REFORMAT_BLOCK;
      text = newText;

      if (LanguageFormatting.INSTANCE.forContext(file) == null
          && indentOptions != CodeInsightSettings.NO_REFORMAT) {
        indentOptions = CodeInsightSettings.INDENT_BLOCK;
      }

      final String _text = text;
      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                @Override
                public void run() {
                  EditorModificationUtil.insertStringAtCaret(editor, _text, false, true);
                }
              });

      int length = text.length();
      int offset = caretModel.getOffset() - length;
      if (offset < 0) {
        length += offset;
        offset = 0;
      }
      final RangeMarker bounds = document.createRangeMarker(offset, offset + length);

      caretModel.moveToOffset(bounds.getEndOffset());
      editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      selectionModel.removeSelection();

      final Ref<Boolean> indented = new Ref<Boolean>(Boolean.FALSE);
      for (Map.Entry<CopyPastePostProcessor, TextBlockTransferableData> e : extraData.entrySet()) {
        //noinspection unchecked
        e.getKey()
            .processTransferableData(project, editor, bounds, caretOffset, indented, e.getValue());
      }

      boolean pastedTextContainsWhiteSpacesOnly =
          CharArrayUtil.shiftForward(document.getCharsSequence(), bounds.getStartOffset(), " \n\t")
              >= bounds.getEndOffset();

      VirtualFile virtualFile = file.getVirtualFile();
      if (!pastedTextContainsWhiteSpacesOnly
          && (virtualFile == null
              || !SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile))) {
        final int indentOptions1 = indentOptions;
        ApplicationManager.getApplication()
            .runWriteAction(
                new Runnable() {
                  @Override
                  public void run() {
                    switch (indentOptions1) {
                      case CodeInsightSettings.INDENT_BLOCK:
                        if (!indented.get()) {
                          indentBlock(
                              project,
                              editor,
                              bounds.getStartOffset(),
                              bounds.getEndOffset(),
                              blockIndentAnchorColumn);
                        }
                        break;

                      case CodeInsightSettings.INDENT_EACH_LINE:
                        if (!indented.get()) {
                          indentEachLine(
                              project, editor, bounds.getStartOffset(), bounds.getEndOffset());
                        }
                        break;

                      case CodeInsightSettings.REFORMAT_BLOCK:
                        indentEachLine(
                            project,
                            editor,
                            bounds.getStartOffset(),
                            bounds
                                .getEndOffset()); // this is needed for example when inserting a
                                                  // comment before method
                        reformatBlock(
                            project, editor, bounds.getStartOffset(), bounds.getEndOffset());
                        break;
                    }
                  }
                });
      }

      if (bounds.isValid()) {
        caretModel.moveToOffset(bounds.getEndOffset());
        editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
        selectionModel.removeSelection();
        editor.putUserData(EditorEx.LAST_PASTED_REGION, TextRange.create(bounds));
      }
    }
  }
  private boolean insideVisibleArea(Editor e, PsiElement targetElement) {
    Rectangle visibleArea = e.getScrollingModel().getVisibleArea();
    Point point = e.logicalPositionToXY(e.offsetToLogicalPosition(targetElement.getTextOffset()));

    return visibleArea.contains(point);
  }
Beispiel #25
0
  private static int findSentenceStart(
      Editor editor,
      CharSequence chars,
      int start,
      int max,
      int dir,
      boolean countCurrent,
      boolean multiple) {
    // Save off the next paragraph since a paragraph is a valid sentence.
    int lline = editor.offsetToLogicalPosition(start).line;
    int np = findNextParagraph(editor, lline, dir, false, multiple);

    int end;
    if (chars.charAt(start) == '\n' && !countCurrent) {
      end = findSentenceEnd(editor, chars, start, max, -1, false, multiple);
    } else {
      end = findSentenceEnd(editor, chars, start, max, -1, true, multiple);
    }
    if (end == start && countCurrent && chars.charAt(end) == '\n') {
      return end;
    }

    int pos = end - 1;
    if (end >= 0) {
      int offset = end + 1;
      while (offset < max) {
        char ch = chars.charAt(offset);
        if (!Character.isWhitespace(ch)) {
          break;
        }
        offset++;
      }

      if (dir > 0) {
        if (offset == start && countCurrent) {
          return offset;
        } else if (offset > start) {
          return offset;
        }
      } else {
        if (offset == start && countCurrent) {
          return offset;
        } else if (offset < start) {
          return offset;
        }
      }
    }

    if (dir > 0) {
      end = findSentenceEnd(editor, chars, start, max, dir, true, multiple);
    } else {
      end = findSentenceEnd(editor, chars, pos, max, dir, countCurrent, multiple);
    }

    int res = end + 1;
    if (end != -1 && (chars.charAt(end) != '\n' || !countCurrent)) {
      while (res < max) {
        char ch = chars.charAt(res);
        if (!Character.isWhitespace(ch)) {
          break;
        }
        res++;
      }
    }

    // Now let's see which to return, the sentence we found or the paragraph we found.
    // This mess returns which ever is closer to our starting point (and in the right direction).
    if (res >= 0 && np >= 0) {
      if (dir > 0) {
        if (np < res || res < start) {
          res = np;
        }
      } else {
        if (np > res || (res >= start && !countCurrent)) {
          res = np;
        }
      }
    } else if (res == -1 && np >= 0) {
      res = np;
    }
    // else we found neither, res already -1

    return res;
  }
Beispiel #26
0
  private static int findSentenceEnd(
      Editor editor,
      CharSequence chars,
      int start,
      int max,
      int dir,
      boolean countCurrent,
      boolean multiple) {
    if (dir > 0 && start >= EditorHelper.getFileSize(editor) - 1) {
      return -1;
    } else if (dir < 0 && start <= 0) {
      return -1;
    }

    // Save off the next paragraph since a paragraph is a valid sentence.
    int lline = editor.offsetToLogicalPosition(start).line;
    int np = findNextParagraph(editor, lline, dir, false, multiple);

    // Sections are also end-of-sentence markers. However, { and } in column 1 don't count.
    // Since our section implementation only supports these and form-feed chars, we'll just
    // check for form-feeds below.

    int res = -1;

    int offset = start;
    boolean found = false;
    // Search forward looking for a candidate end-of-sentence character (., !, or ?)
    while (offset >= 0 && offset < max && !found) {
      char ch = chars.charAt(offset);
      if (".!?".indexOf(ch) >= 0) {
        int end = offset; // Save where we found the punctuation.
        offset++;
        // This can be followed by any number of ), ], ", or ' characters.
        while (offset < max) {
          ch = chars.charAt(offset);
          if (")]\"'".indexOf(ch) == -1) {
            break;
          }

          offset++;
        }

        // The next character must be whitespace for this to be a valid end-of-sentence.
        if (offset >= max || Character.isWhitespace(ch)) {
          // So we have found the end of the next sentence. Now let's see if we ended
          // where we started (or further) on a back search. This will happen if we happen
          // to start this whole search already on a sentence end.
          if (offset - 1 == start && !countCurrent) {
            // Skip back to the sentence end so we can search backward from there
            // for the real previous sentence.
            offset = end;
          } else {
            // Yeah - we found the real end-of-sentence. Save it off.
            res = offset - 1;
            found = true;
          }
        } else {
          // Turned out not to be an end-of-sentence so move back to where we were.
          offset = end;
        }
      } else if (ch == '\n') {
        int end = offset; // Save where we found the punctuation.
        if (dir > 0) {
          offset++;
          while (offset < max) {
            ch = chars.charAt(offset);
            if (ch != '\n') {
              offset--;
              break;
            }
            if (offset == np && (end - 1 != start || countCurrent)) {
              break;
            }
            offset++;
          }

          if (offset == np && (end - 1 != start || countCurrent)) {
            res = end - 1;
            found = true;
          } else if (offset > end) {
            res = offset;
            np = res;
            found = true;
          } else if (offset == end) {
            if (offset > 0 && chars.charAt(offset - 1) == '\n' && countCurrent) {
              res = end;
              np = res;
              found = true;
            }
          }
        } else {
          offset--;
          while (offset >= 0) {
            ch = chars.charAt(offset);
            if (ch != '\n') {
              offset++;
              break;
            }

            offset--;
          }

          if (offset < end) {
            if (end == start && countCurrent) {
              res = end;
            } else {
              res = offset - 1;
            }

            found = true;
          }
        }

        offset = end;
      }
      // Form-feeds are also end-of-sentence markers.
      else if (ch == '\u000C') {
        res = offset;
        found = true;
      }

      offset += dir;
    }

    // Now let's see which to return, the sentence we found or the paragraph we found.
    // This mess returns which ever is closer to our starting point (and in the right direction).
    if (res >= 0 && np >= 0) {
      if (dir > 0) {
        if (np < res || res < start) {
          res = np;
        }
      } else {
        if (np > res || (res >= start && !countCurrent)) {
          res = np;
        }
      }
    }
    /*
    else if (res == -1 && np >= 0)
    {
        res = np;
    }
    */

    return res;
  }
  @Override
  @NotNull
  public AnnotationPlace chooseAnnotationsPlace(@NotNull final PsiElement element) {
    if (!element.isPhysical()) return AnnotationPlace.IN_CODE; // element just created
    if (!element.getManager().isInProject(element)) return AnnotationPlace.EXTERNAL;
    final Project project = myPsiManager.getProject();
    final PsiFile containingFile = element.getContainingFile();
    final VirtualFile virtualFile = containingFile.getVirtualFile();
    LOG.assertTrue(virtualFile != null);
    final List<OrderEntry> entries =
        ProjectRootManager.getInstance(project).getFileIndex().getOrderEntriesForFile(virtualFile);
    if (!entries.isEmpty()) {
      for (OrderEntry entry : entries) {
        if (!(entry instanceof ModuleOrderEntry)) {
          if (AnnotationOrderRootType.getUrls(entry).length > 0) {
            return AnnotationPlace.EXTERNAL;
          }
          break;
        }
      }
    }
    final MyExternalPromptDialog dialog =
        ApplicationManager.getApplication().isUnitTestMode()
                || ApplicationManager.getApplication().isHeadlessEnvironment()
            ? null
            : new MyExternalPromptDialog(project);
    if (dialog != null && dialog.isToBeShown()) {
      final PsiElement highlightElement =
          element instanceof PsiNameIdentifierOwner
              ? ((PsiNameIdentifierOwner) element).getNameIdentifier()
              : element.getNavigationElement();
      LOG.assertTrue(highlightElement != null);
      final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
      final List<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
      final boolean highlight =
          editor != null
              && editor.getDocument()
                  == PsiDocumentManager.getInstance(project).getDocument(containingFile);
      try {
        if (highlight) { // do not highlight for batch inspections
          final EditorColorsManager colorsManager = EditorColorsManager.getInstance();
          final TextAttributes attributes =
              colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
          final TextRange textRange = highlightElement.getTextRange();
          HighlightManager.getInstance(project)
              .addRangeHighlight(
                  editor,
                  textRange.getStartOffset(),
                  textRange.getEndOffset(),
                  attributes,
                  true,
                  highlighters);
          final LogicalPosition logicalPosition =
              editor.offsetToLogicalPosition(textRange.getStartOffset());
          editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.CENTER);
        }

        dialog.show();
        if (dialog.getExitCode() == 2) {
          return AnnotationPlace.EXTERNAL;
        } else if (dialog.getExitCode() == 1) {
          return AnnotationPlace.NOWHERE;
        }

      } finally {
        if (highlight) {
          HighlightManager.getInstance(project)
              .removeSegmentHighlighter(editor, highlighters.get(0));
        }
      }
    } else if (dialog != null) {
      dialog.close(DialogWrapper.OK_EXIT_CODE);
    }
    return AnnotationPlace.IN_CODE;
  }
  public static void verifyCaretAndSelectionState(
      Editor editor, CaretAndSelectionState caretState, String message) {
    boolean hasChecks = false;
    for (int i = 0; i < caretState.carets.size(); i++) {
      EditorTestUtil.CaretInfo expected = caretState.carets.get(i);
      if (expected.position != null || expected.selection != null) {
        hasChecks = true;
        break;
      }
    }
    if (!hasChecks) {
      return; // nothing to check, so we skip caret/selection assertions
    }
    String messageSuffix = message == null ? "" : (message + ": ");
    CaretModel caretModel = editor.getCaretModel();
    List<Caret> allCarets = new ArrayList<Caret>(caretModel.getAllCarets());
    assertEquals(
        messageSuffix + " Unexpected number of carets", caretState.carets.size(), allCarets.size());
    for (int i = 0; i < caretState.carets.size(); i++) {
      String caretDescription =
          caretState.carets.size() == 1
              ? ""
              : "caret " + (i + 1) + "/" + caretState.carets.size() + " ";
      Caret currentCaret = allCarets.get(i);
      int actualCaretLine = editor.getDocument().getLineNumber(currentCaret.getOffset());
      int actualCaretColumn =
          currentCaret.getOffset() - editor.getDocument().getLineStartOffset(actualCaretLine);
      LogicalPosition actualCaretPosition = new LogicalPosition(actualCaretLine, actualCaretColumn);
      int selectionStart = currentCaret.getSelectionStart();
      int selectionEnd = currentCaret.getSelectionEnd();
      LogicalPosition actualSelectionStart = editor.offsetToLogicalPosition(selectionStart);
      LogicalPosition actualSelectionEnd = editor.offsetToLogicalPosition(selectionEnd);
      CaretInfo expected = caretState.carets.get(i);
      if (expected.position != null) {
        assertEquals(
            messageSuffix + caretDescription + "unexpected caret position",
            expected.position,
            actualCaretPosition);
      }
      if (expected.selection != null) {
        LogicalPosition expectedSelectionStart =
            editor.offsetToLogicalPosition(expected.selection.getStartOffset());
        LogicalPosition expectedSelectionEnd =
            editor.offsetToLogicalPosition(expected.selection.getEndOffset());

        assertEquals(
            messageSuffix + caretDescription + "unexpected selection start",
            expectedSelectionStart,
            actualSelectionStart);
        assertEquals(
            messageSuffix + caretDescription + "unexpected selection end",
            expectedSelectionEnd,
            actualSelectionEnd);
      } else {
        assertFalse(
            messageSuffix
                + caretDescription
                + "should has no selection, but was: ("
                + actualSelectionStart
                + ", "
                + actualSelectionEnd
                + ")",
            currentCaret.hasSelection());
      }
    }
  }