void (@NotNull Graphics g, @NotNull Rectangle clip, @NotNull LogicalPosition clipStartPosition, @NotNull VisualPosition clipStartVisualPos, int clipStartOffset, int clipEndOffset) { Color defaultBackground = getBackgroundColor(); if (myEditorComponent.isOpaque()) { g.setColor(defaultBackground); g.fillRect(clip.x, clip.y, clip.width, clip.height); } Color prevBackColor = null; int lineHeight = getLineHeight(); int visibleLine = yPositionToVisibleLine(clip.y); Point position = new Point(0, visibleLine * lineHeight); CharSequence prefixText = myPrefixText == null ? null : new CharArrayCharSequence(myPrefixText); if (clipStartVisualPos.line == 0 && prefixText != null) { Color backColor = myPrefixAttributes.getBackgroundColor(); position.x = drawBackground(g, backColor, prefixText, 0, prefixText.length(), position, myPrefixAttributes.getFontType(), defaultBackground, clip); prevBackColor = backColor; } if (clipStartPosition.line >= myDocument.getLineCount() || clipStartPosition.line < 0) { if (position.x > 0) flushBackground(g, clip); return; } myLastBackgroundPosition = null; myLastBackgroundColor = null; mySelectionStartPosition = null; mySelectionEndPosition = null; int start = clipStartOffset; if (!myPurePaintingMode) { getSoftWrapModel().registerSoftWrapsIfNecessary(); } LineIterator lIterator = createLineIterator(); lIterator.start(start); if (lIterator.atEnd()) { return; } IterationState iterationState = new IterationState(this, start, clipEndOffset, isPaintSelection()); TextAttributes attributes = iterationState.getMergedAttributes(); Color backColor = getBackgroundColor(attributes); int fontType = attributes.getFontType(); int lastLineIndex = Math.max(0, myDocument.getLineCount() - 1); // There is a possible case that we need to draw background from the start of soft wrap-introduced visual line. Given position // has valid 'y' coordinate then at it shouldn't be affected by soft wrap that corresponds to the visual line start offset. // Hence, we store information about soft wrap to be skipped for further processing and adjust 'x' coordinate value if necessary. TIntHashSet softWrapsToSkip = new TIntHashSet(); SoftWrap softWrap = getSoftWrapModel().getSoftWrap(start); if (softWrap != null) { softWrapsToSkip.add(softWrap.getStart()); Color color = null; if (backColor != null && !backColor.equals(defaultBackground)) { color = backColor; } // There is a possible case that target clip points to soft wrap-introduced visual line and that it's an active // line (caret cursor is located on it). We want to draw corresponding 'caret line' background for soft wraps-introduced // virtual space then. if (color == null && position.y == getCaretModel().getVisualPosition().line * getLineHeight()) { color = mySettings.isCaretRowShown() ? getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR) : null; } if (color != null) { drawBackground(g, color, softWrap.getIndentInPixels(), position, defaultBackground, clip); prevBackColor = color; } position.x = softWrap.getIndentInPixels(); } // There is a possible case that caret is located at soft-wrapped line. We don't need to paint caret row background // on a last visual line of that soft-wrapped line then. Below is a holder for the flag that indicates if caret row // background is already drawn. boolean[] caretRowPainted = new boolean[1]; CharSequence text = myDocument.getImmutableCharSequence(); while (!iterationState.atEnd() && !lIterator.atEnd()) { int hEnd = iterationState.getEndOffset(); int lEnd = lIterator.getEnd(); if (hEnd >= lEnd) { FoldRegion collapsedFolderAt = myFoldingModel.getCollapsedRegionAtOffset(start); if (collapsedFolderAt == null) { position.x = drawSoftWrapAwareBackground(g, backColor, prevBackColor, text, start, lEnd - lIterator.getSeparatorLength(), position, fontType, defaultBackground, clip, softWrapsToSkip, caretRowPainted); prevBackColor = backColor; paintAfterLineEndBackgroundSegments(g, iterationState, position, defaultBackground, lineHeight); if (lIterator.getLineNumber() < lastLineIndex) { if (backColor != null && !backColor.equals(defaultBackground)) { g.setColor(backColor); g.fillRect(position.x, position.y, clip.x + clip.width - position.x, lineHeight); } } else { if (iterationState.hasPastFileEndBackgroundSegments()) { paintAfterLineEndBackgroundSegments(g, iterationState, position, defaultBackground, lineHeight); } paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground, caretRowPainted); break; } position.x = 0; if (position.y > clip.y + clip.height) break; position.y += lineHeight; start = lEnd; } else if (collapsedFolderAt.getEndOffset() == clipEndOffset) { drawCollapsedFolderBackground(g, clip, defaultBackground, prevBackColor, position, backColor, fontType, softWrapsToSkip, caretRowPainted, text, collapsedFolderAt); prevBackColor = backColor; } lIterator.advance(); } else { FoldRegion collapsedFolderAt = iterationState.getCurrentFold(); if (collapsedFolderAt != null) { drawCollapsedFolderBackground(g, clip, defaultBackground, prevBackColor, position, backColor, fontType, softWrapsToSkip, caretRowPainted, text, collapsedFolderAt); prevBackColor = backColor; } else if (hEnd > lEnd - lIterator.getSeparatorLength()) { position.x = drawSoftWrapAwareBackground( g, backColor, prevBackColor, text, start, lEnd - lIterator.getSeparatorLength(), position, fontType, defaultBackground, clip, softWrapsToSkip, caretRowPainted ); prevBackColor = backColor; } else { position.x = drawSoftWrapAwareBackground( g, backColor, prevBackColor, text, start, hEnd, position, fontType, defaultBackground, clip, softWrapsToSkip, caretRowPainted ); prevBackColor = backColor; } iterationState.advance(); attributes = iterationState.getMergedAttributes(); backColor = getBackgroundColor(attributes); fontType = attributes.getFontType(); start = iterationState.getStartOffset(); } } flushBackground(g, clip); if (lIterator.getLineNumber() >= lastLineIndex && position.y <= clip.y + clip.height) { paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground, caretRowPainted); } // Perform additional activity if soft wrap is added or removed during repainting. if (mySoftWrapsChanged) { mySoftWrapsChanged = false; clearTextWidthCache(); validateSize(); // Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction. repaintToScreenBottom(EditorUtil.yPositionToLogicalLine(this, position)); // Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn // in accordance with the newly introduced soft wrap(s). myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y); } }