void (int startOffset, int endOffset, EffectDescriptor borderDescriptor) { startOffset = DocumentUtil.alignToCodePointBoundary(myDocument, startOffset); endOffset = DocumentUtil.alignToCodePointBoundary(myDocument, endOffset); FoldRegion foldRegion = myEditor.getFoldingModel().getCollapsedRegionAtOffset(startOffset); if (foldRegion != null && endOffset <= foldRegion.getEndOffset()) return; if (!myClipDetector.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 = borderDescriptor.effectType == EffectType.ROUNDED_BOX; int margin = borderDescriptor.effectType == EffectType.SLIGHTLY_WIDER_BOX ? 1 : 0; myGraphics.setColor(borderDescriptor.effectColor); int startVisualLine = myView.offsetToVisualLine(startOffset, false); int endVisualLine = myView.offsetToVisualLine(endOffset, true); if (startVisualLine == endVisualLine) { int y = myView.visualLineToY(startVisualLine) + myYShift; FloatList ranges = adjustedLogicalRangeToVisualRanges(startOffset, endOffset); for (int i = 0; i < ranges.size() - 1; i += 2) { float startX = myCorrector.singleLineBorderStart(ranges.getFloat(i)); if (startX - margin >= myCorrector.startX(startVisualLine)) { startX -= margin; } float endX = myCorrector.singleLineBorderEnd(ranges.getFloat(i + 1)) + margin; drawSimpleBorder(startX, endX, y, rounded); } } else { FloatList leadingRanges = adjustedLogicalRangeToVisualRanges( startOffset, myView.visualPositionToOffset(new VisualPosition(startVisualLine, Integer.MAX_VALUE, true))); FloatList trailingRanges = adjustedLogicalRangeToVisualRanges(myView.visualLineToOffset(endVisualLine), endOffset); if (!leadingRanges.isEmpty() && !trailingRanges.isEmpty()) { int minX = Math.min(myCorrector.minX(startVisualLine, endVisualLine), (int)leadingRanges.getFloat(0)); int maxX = Math.max(myCorrector.maxX(startVisualLine, endVisualLine), (int)trailingRanges.getFloat(trailingRanges.size() - 1)); boolean containsInnerLines = endVisualLine > startVisualLine + 1; int lineHeight = myLineHeight - 1; int leadingTopY = myView.visualLineToY(startVisualLine) + myYShift; int leadingBottomY = leadingTopY + lineHeight; int trailingTopY = myView.visualLineToY(endVisualLine) + myYShift; int trailingBottomY = trailingTopY + lineHeight; float start = 0; float end = 0; float leftGap = leadingRanges.getFloat(0) - (containsInnerLines ? minX : trailingRanges.getFloat(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.getFloat(i); end = leadingRanges.getFloat(i + 1); if (i > 0) { drawLine(leadingRanges.getFloat(i - 1), leadingBottomY, start, leadingBottomY, rounded); } drawLine(start, leadingBottomY + (i == 0 ? adjustY : 0), start, leadingTopY, rounded); if ((i + 2) < leadingRanges.size()) { drawLine(start, leadingTopY, end, leadingTopY, rounded); drawLine(end, leadingTopY, end, leadingBottomY, rounded); } } end = Math.max(end, maxX); drawLine(start, leadingTopY, end, leadingTopY, rounded); drawLine(end, leadingTopY, end, trailingTopY - 1, rounded); float targetX = trailingRanges.getFloat(trailingRanges.size() - 1); drawLine(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.getFloat(i); end = trailingRanges.getFloat(i + 1); drawLine(end, trailingTopY + (i == 0 ? adjustY : 0), end, trailingBottomY, rounded); drawLine(end, trailingBottomY, start, trailingBottomY, rounded); drawLine(start, trailingBottomY, start, trailingTopY, rounded); if (i > 0) { drawLine(start, trailingTopY, trailingRanges.getFloat(i - 1), trailingTopY, rounded); } } float lastX = start; if (containsInnerLines) { if (start != minX) { drawLine(start, trailingTopY, start, trailingTopY - 1, rounded); drawLine(start, trailingTopY - 1, minX, trailingTopY - 1, rounded); drawLine(minX, trailingTopY - 1, minX, leadingBottomY + 1, rounded); } else { drawLine(minX, trailingTopY, minX, leadingBottomY + 1, rounded); } lastX = minX; } targetX = leadingRanges.getFloat(0); if (lastX < targetX) { drawLine(lastX, leadingBottomY + 1, targetX, leadingBottomY + 1, rounded); } else { drawLine(lastX, leadingBottomY + 1, lastX, leadingBottomY, rounded); drawLine(lastX, leadingBottomY, targetX, leadingBottomY, rounded); } } } }