private ProperTextRange offsetToYPosition(int start, int end) { if (myEditorScrollbarTop == -1 || myEditorTargetHeight == -1) { recalcEditorDimensions(); } Document document = myEditor.getDocument(); int startLineNumber = offsetToLine(start, document); int startY; int lineCount; if (myEditorSourceHeight < myEditorTargetHeight) { lineCount = 0; startY = myEditorScrollbarTop + startLineNumber * myEditor.getLineHeight(); } else { lineCount = myEditorSourceHeight / myEditor.getLineHeight(); startY = myEditorScrollbarTop + (int) ((float) startLineNumber / lineCount * myEditorTargetHeight); } int endY; if (document.getLineNumber(start) == document.getLineNumber(end)) { endY = startY; // both offsets are on the same line, no need to recalc Y position } else { int endLineNumber = offsetToLine(end, document); if (myEditorSourceHeight < myEditorTargetHeight) { endY = myEditorScrollbarTop + endLineNumber * myEditor.getLineHeight(); } else { endY = myEditorScrollbarTop + (int) ((float) endLineNumber / lineCount * myEditorTargetHeight); } if (endY < startY) endY = startY; } return new ProperTextRange(startY, endY); }
public void doClick(final MouseEvent e, final int width) { RangeHighlighter marker = getNearestRangeHighlighter(e, width); if (marker == null) return; int offset = marker.getStartOffset(); final Document doc = myEditor.getDocument(); if (doc.getLineCount() > 0) { // Necessary to expand folded block even if navigating just before one // Very useful when navigating to first unused import statement. int lineEnd = doc.getLineEndOffset(doc.getLineNumber(offset)); myEditor.getCaretModel().moveToOffset(lineEnd); } myEditor.getCaretModel().moveToOffset(offset); myEditor.getSelectionModel().removeSelection(); ScrollingModel scrollingModel = myEditor.getScrollingModel(); scrollingModel.disableAnimation(); scrollingModel.scrollToCaret(ScrollType.CENTER); scrollingModel.enableAnimation(); fireErrorMarkerClicked(marker, e); }
@Override @SuppressWarnings({"AssignmentToForLoopParameter"}) public void paint( @NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) { int startOffset = highlighter.getStartOffset(); final Document doc = highlighter.getDocument(); if (startOffset >= doc.getTextLength()) return; final int endOffset = highlighter.getEndOffset(); final int endLine = doc.getLineNumber(endOffset); int off; int startLine = doc.getLineNumber(startOffset); IndentGuideDescriptor descriptor = editor.getIndentsModel().getDescriptor(startLine, endLine); final CharSequence chars = doc.getCharsSequence(); do { int start = doc.getLineStartOffset(startLine); int end = doc.getLineEndOffset(startLine); off = CharArrayUtil.shiftForward(chars, start, end, " \t"); startLine--; } while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n'); final VisualPosition startPosition = editor.offsetToVisualPosition(off); int indentColumn = startPosition.column; // It's considered that indent guide can cross not only white space but comments, javadocs // etc. Hence, there is a possible // case that the first indent guide line is, say, single-line comment where comment // symbols ('//') are located at the first // visual column. We need to calculate correct indent guide column then. int lineShift = 1; if (indentColumn <= 0 && descriptor != null) { indentColumn = descriptor.indentLevel; lineShift = 0; } if (indentColumn <= 0) return; final FoldingModel foldingModel = editor.getFoldingModel(); if (foldingModel.isOffsetCollapsed(off)) return; final FoldRegion headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off))); final FoldRegion tailRegion = foldingModel.getCollapsedRegionAtOffset( doc.getLineStartOffset(doc.getLineNumber(endOffset))); if (tailRegion != null && tailRegion == headerRegion) return; final boolean selected; final IndentGuideDescriptor guide = editor.getIndentsModel().getCaretIndentGuide(); if (guide != null) { final CaretModel caretModel = editor.getCaretModel(); final int caretOffset = caretModel.getOffset(); selected = caretOffset >= off && caretOffset < endOffset && caretModel.getLogicalPosition().column == indentColumn; } else { selected = false; } Point start = editor.visualPositionToXY( new VisualPosition(startPosition.line + lineShift, indentColumn)); final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset); Point end = editor.visualPositionToXY(new VisualPosition(endPosition.line, endPosition.column)); int maxY = end.y; if (endPosition.line == editor.offsetToVisualPosition(doc.getTextLength()).line) { maxY += editor.getLineHeight(); } Rectangle clip = g.getClipBounds(); if (clip != null) { if (clip.y >= maxY || clip.y + clip.height <= start.y) { return; } maxY = Math.min(maxY, clip.y + clip.height); } final EditorColorsScheme scheme = editor.getColorsScheme(); g.setColor( selected ? scheme.getColor(EditorColors.SELECTED_INDENT_GUIDE_COLOR) : scheme.getColor(EditorColors.INDENT_GUIDE_COLOR)); // There is a possible case that indent line intersects soft wrap-introduced text. // Example: // this is a long line <soft-wrap> // that| is soft-wrapped // | // | <- vertical indent // // Also it's possible that no additional intersections are added because of soft wrap: // this is a long line <soft-wrap> // | that is soft-wrapped // | // | <- vertical indent // We want to use the following approach then: // 1. Show only active indent if it crosses soft wrap-introduced text; // 2. Show indent as is if it doesn't intersect with soft wrap-introduced text; if (selected) { g.drawLine(start.x + 2, start.y, start.x + 2, maxY); } else { int y = start.y; int newY = start.y; SoftWrapModel softWrapModel = editor.getSoftWrapModel(); int lineHeight = editor.getLineHeight(); for (int i = Math.max(0, startLine + lineShift); i < endLine && newY < maxY; i++) { List<? extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForLine(i); int logicalLineHeight = softWraps.size() * lineHeight; if (i > startLine + lineShift) { logicalLineHeight += lineHeight; // We assume that initial 'y' value points just below the target // line. } if (!softWraps.isEmpty() && softWraps.get(0).getIndentInColumns() < indentColumn) { if (y < newY || i > startLine + lineShift) { // There is a possible case that soft wrap is located on // indent start line. g.drawLine(start.x + 2, y, start.x + 2, newY + lineHeight); } newY += logicalLineHeight; y = newY; } else { newY += logicalLineHeight; } FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(i)); if (foldRegion != null && foldRegion.getEndOffset() < doc.getTextLength()) { i = doc.getLineNumber(foldRegion.getEndOffset()); } } if (y < maxY) { g.drawLine(start.x + 2, y, start.x + 2, maxY); } } }
public void doWrapLongLinesIfNecessary( @NotNull final Editor editor, @NotNull final Project project, @NotNull Document document, int startOffset, int endOffset) { // Normalization. int startOffsetToUse = Math.min(document.getTextLength(), Math.max(0, startOffset)); int endOffsetToUse = Math.min(document.getTextLength(), Math.max(0, endOffset)); LineWrapPositionStrategy strategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(editor); CharSequence text = document.getCharsSequence(); int startLine = document.getLineNumber(startOffsetToUse); int endLine = document.getLineNumber(Math.max(0, endOffsetToUse - 1)); int maxLine = Math.min(document.getLineCount(), endLine + 1); int tabSize = EditorUtil.getTabSize(editor); if (tabSize <= 0) { tabSize = 1; } int spaceSize = EditorUtil.getSpaceWidth(Font.PLAIN, editor); int[] shifts = new int[2]; // shifts[0] - lines shift. // shift[1] - offset shift. for (int line = startLine; line < maxLine; line++) { int startLineOffset = document.getLineStartOffset(line); int endLineOffset = document.getLineEndOffset(line); final int preferredWrapPosition = calculatePreferredWrapPosition( editor, text, tabSize, spaceSize, startLineOffset, endLineOffset, endOffsetToUse); if (preferredWrapPosition < 0 || preferredWrapPosition >= endLineOffset) { continue; } if (preferredWrapPosition >= endOffsetToUse) { return; } // We know that current line exceeds right margin if control flow reaches this place, so, wrap // it. int wrapOffset = strategy.calculateWrapPosition( document, editor.getProject(), Math.max(startLineOffset, startOffsetToUse), Math.min(endLineOffset, endOffsetToUse), preferredWrapPosition, false, false); if (wrapOffset < 0 // No appropriate wrap position is found. // No point in splitting line when its left part contains only white spaces, example: // line start -> | | <- right margin // | aaaaaaaaaaaaaaaa|aaaaaaaaaaaaaaaaaaaa() <- don't want to wrap this // line even if it exceeds right margin || CharArrayUtil.shiftBackward(text, startLineOffset, wrapOffset - 1, " \t") < startLineOffset) { continue; } // Move caret to the target position and emulate pressing <enter>. editor.getCaretModel().moveToOffset(wrapOffset); emulateEnter(editor, project, shifts); // We know that number of lines is just increased, hence, update the data accordingly. maxLine += shifts[0]; endOffsetToUse += shifts[1]; } }