void () { int lineCount = myEditor.getVisibleLineCount(); boolean calculateMarginWidths = Registry.is("editor.adjust.right.margin") && isMarginShown() && myStartVisualLine < lineCount; myMarginPositions = calculateMarginWidths ? new MarginPositions(Math.min(myEndVisualLine, lineCount - 1) - myStartVisualLine + 2) : null; final LineWhitespacePaintingStrategy whitespacePaintingStrategy = new LineWhitespacePaintingStrategy(myEditor.getSettings()); boolean paintAllSoftWraps = myEditor.getSettings().isAllSoftWrapsShown(); float whiteSpaceScale = getWhiteSpaceScale(myEditor); final BasicStroke whiteSpaceStroke = new BasicStroke(calcFeatureSize(1, whiteSpaceScale)); PeekableIterator<Caret> caretIterator = null; if (myEditor.getInlayModel().hasBlockElements()) { Iterator<Caret> carets = myEditor.getCaretModel().getAllCarets() .stream() .filter(Caret::hasSelection) .sorted(Comparator.comparingInt(Caret::getSelectionStart)) .iterator(); caretIterator = new PeekableIteratorWrapper<>(carets); } final VisualPosition primarySelectionStart = myEditor.getSelectionModel().getSelectionStartPosition(); final VisualPosition primarySelectionEnd = myEditor.getSelectionModel().getSelectionEndPosition(); LineLayout prefixLayout = myView.getPrefixLayout(); if (myStartVisualLine == 0 && prefixLayout != null) { float width = prefixLayout.getWidth(); TextAttributes attributes = myView.getPrefixAttributes(); paintBackground(attributes, myCorrector.startX(myStartVisualLine), myYShift + myView.visualLineToY(0), width); myTextDrawingTasks.add(g -> { paintLineLayoutWithEffect(prefixLayout, myCorrector.startX(myStartVisualLine), myAscent + myYShift + myView.visualLineToY(0), attributes.getForegroundColor(), attributes.getEffectColor(), attributes.getEffectType()); }); } int startX = myInsets.left; int endX = myClip.x + myClip.width; int prevY = Math.max(myInsets.top, myClip.y) + myYShift; VisualLinesIterator visLinesIterator = new VisualLinesIterator(myEditor, myStartVisualLine); while (!visLinesIterator.atEnd()) { int visualLine = visLinesIterator.getVisualLine(); if (visualLine > myEndVisualLine + 1) break; int y = visLinesIterator.getY() + myYShift; if (calculateMarginWidths) myMarginPositions.y[visualLine - myStartVisualLine] = y; if (y > prevY) { TextAttributes attributes = getBetweenLinesAttributes(visualLine, visLinesIterator.getVisualLineStartOffset(), Objects.requireNonNull(caretIterator)); myBetweenLinesAttributes.put(visualLine, attributes); paintBackground(attributes.getBackgroundColor(), startX, prevY, endX - startX, y - prevY); } boolean dryRun = visualLine > myEndVisualLine; if (dryRun && !calculateMarginWidths) break; boolean paintSoftWraps = paintAllSoftWraps || myEditor.getCaretModel().getLogicalPosition().line == visLinesIterator.getDisplayedLogicalLine(); int[] currentLogicalLine = new int[]{-1}; paintLineFragments(visLinesIterator, y, new LineFragmentPainter() { @Override public void paintBeforeLineStart(TextAttributes attributes, boolean hasSoftWrap, int columnEnd, float xEnd, int y) { if (dryRun) return; if (visualLine == 0) xEnd -= myView.getPrefixTextWidthInPixels(); paintBackground(attributes, startX, y, xEnd); if (!hasSoftWrap) return; paintSelectionOnSecondSoftWrapLineIfNecessary(visualLine, columnEnd, xEnd, y, primarySelectionStart, primarySelectionEnd); if (paintSoftWraps) { int x = (int)xEnd; myTextDrawingTasks.add(g -> { SoftWrapModelImpl softWrapModel = myEditor.getSoftWrapModel(); int symbolWidth = softWrapModel.getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP); softWrapModel.doPaint(g, SoftWrapDrawingType.AFTER_SOFT_WRAP, x - symbolWidth, y, myLineHeight); }); } } @Override public void paint(VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes, float xStart, float xEnd, int y) { if (dryRun) return; FoldRegion foldRegion = fragment.getCurrentFoldRegion(); TextAttributes foldRegionInnerAttributes = foldRegion == null || !Registry.is("editor.highlight.foldings") ? null : getInnerHighlighterAttributes(foldRegion); if (foldRegionInnerAttributes == null || !paintFoldingBackground(foldRegionInnerAttributes, xStart, y, xEnd - xStart, foldRegion)) { paintBackground(attributes, xStart, y, xEnd - xStart); } Inlay inlay = fragment.getCurrentInlay(); if (inlay != null) { TextAttributes attrs = attributes.clone(); myTextDrawingTasks.add(g -> { inlay.getRenderer().paint(inlay, g, new Rectangle2D.Double(xStart, y, xEnd - xStart, myLineHeight), attrs); }); } else { if (foldRegionInnerAttributes != null) { attributes = TextAttributes.merge(attributes, foldRegionInnerAttributes); } if (attributes != null) { attributes.forEachEffect((type, color) -> myTextDrawingTasks.add( g -> paintTextEffect(xStart, xEnd, y + myAscent, color, type, foldRegion != null) )); } if (attributes != null) { Color color = attributes.getForegroundColor(); if (color != null) { myTextDrawingTasks.add(g -> g.setColor(color)); myTextDrawingTasks.add(fragment.draw(xStart, y + myAscent, start, end)); } } } if (foldRegion == null) { int logicalLine = fragment.getStartLogicalLine(); if (logicalLine != currentLogicalLine[0]) { whitespacePaintingStrategy.update(myText, myDocument.getLineStartOffset(logicalLine), myDocument.getLineEndOffset(logicalLine)); currentLogicalLine[0] = logicalLine; } paintWhitespace(xStart, y + myAscent, start, end, whitespacePaintingStrategy, fragment, whiteSpaceStroke, whiteSpaceScale); } } @Override public void paintAfterLineEnd(IterationState it, int columnStart, float x, int y) { if (dryRun) return; TextAttributes backgroundAttributes = it.getPastLineEndBackgroundAttributes().clone(); CustomFoldRegion cfr = visLinesIterator.getCustomFoldRegion(); if (cfr != null) { paintBackground(backgroundAttributes, startX, y, endX - startX, cfr.getHeightInPixels()); myTextDrawingTasks.add(g -> { cfr.getRenderer().paint(cfr, g, new Rectangle2D.Double(x, y, cfr.getWidthInPixels(), cfr.getHeightInPixels()), backgroundAttributes); }); return; } paintBackground(backgroundAttributes, x, y, endX - x); int offset = it.getEndOffset(); SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset); if (softWrap == null) { int logicalLine = myDocument.getLineNumber(offset); List<Inlay<?>> inlays = myEditor.getInlayModel().getAfterLineEndElementsForLogicalLine(logicalLine); float extensionsStartX = inlays.isEmpty() ? x : x + myView.getPlainSpaceWidth() + inlays.stream().mapToInt(Inlay::getWidthInPixels).sum(); collectExtensions(visualLine, offset); paintLineExtensionsBackground(visualLine, extensionsStartX, y); paintVirtualSelectionIfNecessary(visualLine, columnStart, x, y); myTextDrawingTasks.add(g -> { if (!inlays.isEmpty()) { float curX = x + myView.getPlainSpaceWidth(); for (Inlay inlay : inlays) { int width = inlay.getWidthInPixels(); inlay.getRenderer().paint(inlay, g, new Rectangle2D.Double(curX, y, width, myLineHeight), backgroundAttributes); curX += width; } } paintLineExtensions(visualLine, logicalLine, extensionsStartX, y + myAscent); }); } else { paintSelectionOnFirstSoftWrapLineIfNecessary(visualLine, columnStart, x, y, primarySelectionStart, primarySelectionEnd); if (paintSoftWraps) { myTextDrawingTasks.add(g -> { myEditor.getSoftWrapModel().doPaint(g, SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED, (int)x, y, myLineHeight); }); } } } }, calculateMarginWidths && !visLinesIterator.endsWithSoftWrap() && !visLinesIterator.startsWithSoftWrap() ? width -> myMarginPositions.x[visualLine - myStartVisualLine] = width : null); prevY = y + visLinesIterator.getLineHeight(); visLinesIterator.advance(); } if (calculateMarginWidths && myEndVisualLine >= lineCount - 1) { myMarginPositions.y[myMarginPositions.y.length - 1] = myMarginPositions.y[myMarginPositions.y.length - 2] + myLineHeight; } }