/**
  * Returns a list of pairs of x coordinates for visual ranges representing given logical range. If
  * <code>startOffset == endOffset</code>, a pair of equal numbers is returned, corresponding to
  * target position. Target offsets are supposed to be located on the same visual line.
  */
 private TFloatArrayList logicalRangeToVisualRanges(int startOffset, int endOffset) {
   assert startOffset <= endOffset;
   TFloatArrayList result = new TFloatArrayList();
   for (VisualLineFragmentsIterator.Fragment fragment :
       VisualLineFragmentsIterator.create(myView, startOffset, false)) {
     int minOffset = fragment.getMinOffset();
     int maxOffset = fragment.getMaxOffset();
     if (startOffset == endOffset) {
       if (startOffset >= minOffset && startOffset <= maxOffset) {
         float x = fragment.offsetToX(startOffset);
         result.add(x);
         result.add(x);
         break;
       }
     } else if (startOffset < maxOffset && endOffset > minOffset) {
       float x1 = fragment.offsetToX(Math.max(minOffset, startOffset));
       float x2 = fragment.offsetToX(Math.min(maxOffset, endOffset));
       if (x1 > x2) {
         float tmp = x1;
         x1 = x2;
         x2 = tmp;
       }
       if (result.isEmpty() || x1 > result.get(result.size() - 1)) {
         result.add(x1);
         result.add(x2);
       } else {
         result.set(result.size() - 1, x2);
       }
     }
   }
   return result;
 }
 private void paintVirtualSelectionIfNecessary(
     Graphics2D g,
     Map<Integer, Couple<Integer>> virtualSelectionMap,
     int columnStart,
     float xStart,
     float xEnd,
     int y) {
   int visualLine = myView.yToVisualLine(y);
   Couple<Integer> selectionRange = virtualSelectionMap.get(visualLine);
   if (selectionRange == null || selectionRange.second <= columnStart) return;
   float startX =
       selectionRange.first <= columnStart
           ? xStart
           : myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.first)).x;
   float endX =
       Math.min(
           xEnd,
           myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.second)).x);
   paintBackground(
       g,
       myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
       startX,
       y,
       endX - startX);
 }
 private static void duplicateHighlighters(
     MarkupModel to, MarkupModel from, int offset, TextRange textRange) {
   for (RangeHighlighter rangeHighlighter : from.getAllHighlighters()) {
     if (!rangeHighlighter.isValid()) continue;
     Object tooltip = rangeHighlighter.getErrorStripeTooltip();
     HighlightInfo highlightInfo =
         tooltip instanceof HighlightInfo ? (HighlightInfo) tooltip : null;
     if (highlightInfo != null) {
       if (highlightInfo.getSeverity() != HighlightSeverity.INFORMATION) continue;
       if (highlightInfo.type.getAttributesKey() == EditorColors.IDENTIFIER_UNDER_CARET_ATTRIBUTES)
         continue;
     }
     final int localOffset = textRange.getStartOffset();
     final int start = Math.max(rangeHighlighter.getStartOffset(), localOffset) - localOffset;
     final int end =
         Math.min(rangeHighlighter.getEndOffset(), textRange.getEndOffset()) - localOffset;
     if (start > end) continue;
     final RangeHighlighter h =
         to.addRangeHighlighter(
             start + offset,
             end + offset,
             rangeHighlighter.getLayer(),
             rangeHighlighter.getTextAttributes(),
             rangeHighlighter.getTargetArea());
     ((RangeHighlighterEx) h)
         .setAfterEndOfLine(((RangeHighlighterEx) rangeHighlighter).isAfterEndOfLine());
   }
 }
  private void paintComposedTextDecoration(Graphics2D g) {
    TextRange composedTextRange = myEditor.getComposedTextRange();
    if (composedTextRange != null) {
      Point p1 =
          myView.offsetToXY(
              Math.min(composedTextRange.getStartOffset(), myDocument.getTextLength()),
              true,
              false);
      Point p2 =
          myView.offsetToXY(
              Math.min(composedTextRange.getEndOffset(), myDocument.getTextLength()), false, true);

      int y = p1.y + myView.getAscent() + 1;

      g.setStroke(IME_COMPOSED_TEXT_UNDERLINE_STROKE);
      g.setColor(myEditor.getColorsScheme().getDefaultForeground());
      UIUtil.drawLine(g, p1.x, y, p2.x, y);
    }
  }
  private void paintLineMarkerSeparator(RangeHighlighter marker, Rectangle clip, Graphics g) {
    Color separatorColor = marker.getLineSeparatorColor();
    LineSeparatorRenderer lineSeparatorRenderer = marker.getLineSeparatorRenderer();
    if (separatorColor == null && lineSeparatorRenderer == null) {
      return;
    }
    int line =
        myDocument.getLineNumber(
            marker.getLineSeparatorPlacement() == SeparatorPlacement.TOP
                ? marker.getStartOffset()
                : marker.getEndOffset());
    int visualLine =
        myView.logicalToVisualPosition(
                new LogicalPosition(
                    line + (marker.getLineSeparatorPlacement() == SeparatorPlacement.TOP ? 0 : 1),
                    0),
                false)
            .line;
    int y = myView.visualLineToY(visualLine) - 1;
    int endShift = clip.x + clip.width;
    EditorSettings settings = myEditor.getSettings();
    if (settings.isRightMarginShown()
        && myEditor.getColorsScheme().getColor(EditorColors.RIGHT_MARGIN_COLOR) != null) {
      endShift =
          Math.min(
              endShift,
              settings.getRightMargin(myEditor.getProject()) * myView.getPlainSpaceWidth());
    }

    g.setColor(separatorColor);
    if (lineSeparatorRenderer != null) {
      lineSeparatorRenderer.drawLine(g, 0, endShift, y);
    } else {
      UIUtil.drawLine(g, 0, y, endShift, y);
    }
  }
 private void paintLineFragments(
     Graphics2D g, Rectangle clip, int visualLine, int y, LineFragmentPainter painter) {
   float x = visualLine == 0 ? myView.getPrefixTextWidthInPixels() : 0;
   int offset = myView.visualPositionToOffset(new VisualPosition(visualLine, 0));
   int visualLineEndOffset =
       myView.visualPositionToOffset(new VisualPosition(visualLine, Integer.MAX_VALUE, true));
   IterationState it = null;
   int prevEndOffset = -1;
   boolean firstFragment = true;
   int maxColumn = 0;
   for (VisualLineFragmentsIterator.Fragment fragment :
       VisualLineFragmentsIterator.create(myView, offset, false)) {
     int fragmentStartOffset = fragment.getStartOffset();
     int start = fragmentStartOffset;
     int end = fragment.getEndOffset();
     x = fragment.getStartX();
     if (firstFragment) {
       firstFragment = false;
       SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset);
       if (softWrap != null) {
         prevEndOffset = offset;
         it =
             new IterationState(
                 myEditor,
                 offset == 0 ? 0 : offset - 1,
                 visualLineEndOffset,
                 true,
                 false,
                 false,
                 false);
         if (it.getEndOffset() <= offset) {
           it.advance();
         }
         painter.paintBeforeLineStart(
             g,
             it.getStartOffset() == offset
                 ? it.getBeforeLineStartBackgroundAttributes()
                 : it.getMergedAttributes(),
             fragment.getStartVisualColumn(),
             fragment.getStartX(),
             y);
       }
     }
     FoldRegion foldRegion = fragment.getCurrentFoldRegion();
     if (foldRegion == null) {
       if (start != prevEndOffset) {
         it =
             new IterationState(
                 myEditor,
                 start,
                 fragment.isRtl() ? offset : visualLineEndOffset,
                 true,
                 false,
                 false,
                 fragment.isRtl());
       }
       prevEndOffset = end;
       assert it != null;
       while (fragment.isRtl() ? start > end : start < end) {
         if (fragment.isRtl() ? it.getEndOffset() >= start : it.getEndOffset() <= start) {
           assert !it.atEnd();
           it.advance();
         }
         TextAttributes attributes = it.getMergedAttributes();
         int curEnd =
             fragment.isRtl()
                 ? Math.max(it.getEndOffset(), end)
                 : Math.min(it.getEndOffset(), end);
         float xNew = fragment.offsetToX(x, start, curEnd);
         painter.paint(
             g,
             fragment,
             fragment.isRtl() ? fragmentStartOffset - start : start - fragmentStartOffset,
             fragment.isRtl() ? fragmentStartOffset - curEnd : curEnd - fragmentStartOffset,
             attributes,
             x,
             xNew,
             y);
         x = xNew;
         start = curEnd;
       }
     } else {
       float xNew = fragment.getEndX();
       painter.paint(
           g,
           fragment,
           0,
           fragment.getEndVisualColumn() - fragment.getStartVisualColumn(),
           getFoldRegionAttributes(foldRegion),
           x,
           xNew,
           y);
       x = xNew;
       prevEndOffset = -1;
       it = null;
     }
     maxColumn = fragment.getEndVisualColumn();
   }
   if (it == null || it.getEndOffset() != visualLineEndOffset) {
     it =
         new IterationState(
             myEditor,
             visualLineEndOffset == offset ? visualLineEndOffset : visualLineEndOffset - 1,
             visualLineEndOffset,
             true,
             false,
             false,
             false);
   }
   if (!it.atEnd()) {
     it.advance();
   }
   assert it.atEnd();
   painter.paintAfterLineEnd(g, clip, it, maxColumn, x, y);
 }
  protected String addTextRangeToHistory(
      TextRange textRange, final EditorEx consoleEditor, boolean preserveMarkup) {
    final Document history = myHistoryViewer.getDocument();
    final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
    if (myPrompt != null) {
      appendToHistoryDocument(history, myPrompt);
    }
    markupModel.addRangeHighlighter(
        history.getTextLength() - StringUtil.length(myPrompt),
        history.getTextLength(),
        HighlighterLayer.SYNTAX,
        ConsoleViewContentType.USER_INPUT.getAttributes(),
        HighlighterTargetArea.EXACT_RANGE);
    final int localStartOffset = textRange.getStartOffset();
    String text;
    EditorHighlighter highlighter;
    if (consoleEditor instanceof EditorWindow) {
      EditorWindow editorWindow = (EditorWindow) consoleEditor;
      EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
      PsiFile file = editorWindow.getInjectedFile();
      final VirtualFile virtualFile = file.getVirtualFile();
      assert virtualFile != null;
      highlighter = HighlighterFactory.createHighlighter(virtualFile, scheme, getProject());
      String fullText = InjectedLanguageUtil.getUnescapedText(file, null, null);
      highlighter.setText(fullText);
      text = textRange.substring(fullText);
    } else {
      text = consoleEditor.getDocument().getText(textRange);
      highlighter = consoleEditor.getHighlighter();
    }
    // offset can be changed after text trimming after insert due to buffer constraints
    appendToHistoryDocument(history, text);
    int offset = history.getTextLength() - text.length();

    final HighlighterIterator iterator = highlighter.createIterator(localStartOffset);
    final int localEndOffset = textRange.getEndOffset();
    while (!iterator.atEnd()) {
      final int itStart = iterator.getStart();
      if (itStart > localEndOffset) break;
      final int itEnd = iterator.getEnd();
      if (itEnd >= localStartOffset) {
        final int start = Math.max(itStart, localStartOffset) - localStartOffset + offset;
        final int end = Math.min(itEnd, localEndOffset) - localStartOffset + offset;
        markupModel.addRangeHighlighter(
            start,
            end,
            HighlighterLayer.SYNTAX,
            iterator.getTextAttributes(),
            HighlighterTargetArea.EXACT_RANGE);
      }
      iterator.advance();
    }
    if (preserveMarkup) {
      duplicateHighlighters(
          markupModel,
          DocumentMarkupModel.forDocument(consoleEditor.getDocument(), myProject, true),
          offset,
          textRange);
      duplicateHighlighters(markupModel, consoleEditor.getMarkupModel(), offset, textRange);
    }
    if (!text.endsWith("\n")) {
      appendToHistoryDocument(history, "\n");
    }
    return text;
  }