/** * Configures given editor to wrap at given width, assuming characters are of given width * * @return whether any actual wraps of editor contents were created as a result of turning on soft * wraps */ @TestOnly public static boolean configureSoftWraps( Editor editor, final int visibleWidth, final int charWidthInPixels) { editor.getSettings().setUseSoftWraps(true); SoftWrapModelImpl model = (SoftWrapModelImpl) editor.getSoftWrapModel(); model.setSoftWrapPainter( new SoftWrapPainter() { @Override public int paint( @NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { return charWidthInPixels; } @Override public int getDrawingHorizontalOffset( @NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { return charWidthInPixels; } @Override public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) { return charWidthInPixels; } @Override public boolean canUse() { return true; } @Override public void reinit() {} }); model.reinitSettings(); SoftWrapApplianceManager applianceManager = model.getApplianceManager(); applianceManager.setWidthProvider( new SoftWrapApplianceManager.VisibleAreaWidthProvider() { @Override public int getVisibleAreaWidth() { return visibleWidth; } }); model.setEditorTextRepresentationHelper( new DefaultEditorTextRepresentationHelper(editor) { @Override public int charWidth(char c, int fontType) { return charWidthInPixels; } }); applianceManager.registerSoftWrapIfNecessary(); return !model.getRegisteredSoftWraps().isEmpty(); }
@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 static EditorTextRepresentationHelper getEditorTextRepresentationHelper( @NotNull Editor editor) { return ((SoftWrapModelEx) editor.getSoftWrapModel()).getEditorTextRepresentationHelper(); }
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { myProject = project; myFile = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage()); myEditor = editor; PsiElement context = myFile.getContext(); if (context != null && (context.textContains('\'') || context.textContains('\"'))) { String s = context.getText(); if (StringUtil.startsWith(s, "\"") || StringUtil.startsWith(s, "\'")) { myFile = context.getContainingFile(); myEditor = editor instanceof EditorWindow ? ((EditorWindow) editor).getDelegate() : editor; } } myDocument = myEditor.getDocument(); if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) { return; } PsiDocumentManager.getInstance(project).commitDocument(myDocument); FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line"); myCodeStyleManager = CodeStyleManager.getInstance(myProject); final SelectionModel selectionModel = myEditor.getSelectionModel(); boolean hasSelection = selectionModel.hasSelection(); myStartOffset = selectionModel.getSelectionStart(); myEndOffset = selectionModel.getSelectionEnd(); FoldRegion fold = myEditor.getFoldingModel().getCollapsedRegionAtOffset(myStartOffset); if (fold != null && fold.shouldNeverExpand() && fold.getStartOffset() == myStartOffset && fold.getEndOffset() == myEndOffset) { // Foldings that never expand are automatically selected, so the fact it is selected must not // interfer with commenter's logic hasSelection = false; } if (myDocument.getTextLength() == 0) return; while (true) { int lastLineEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset)); FoldRegion collapsedAt = myEditor.getFoldingModel().getCollapsedRegionAtOffset(lastLineEnd); if (collapsedAt != null) { final int endOffset = collapsedAt.getEndOffset(); if (endOffset <= myEndOffset) { break; } myEndOffset = endOffset; } else { break; } } boolean wholeLinesSelected = !hasSelection || myStartOffset == myDocument.getLineStartOffset(myDocument.getLineNumber(myStartOffset)) && myEndOffset == myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset - 1)) + 1; boolean startingNewLineComment = !hasSelection && isLineEmpty(myDocument.getLineNumber(myStartOffset)) && !Comparing.equal( IdeActions.ACTION_COMMENT_LINE, ActionManagerEx.getInstanceEx().getPrevPreformedActionId()); doComment(); if (startingNewLineComment) { final Commenter commenter = myCommenters[0]; if (commenter != null) { String prefix; if (commenter instanceof SelfManagingCommenter) { prefix = ((SelfManagingCommenter) commenter) .getCommentPrefix( myStartLine, myDocument, myCommenterStateMap.get((SelfManagingCommenter) commenter)); if (prefix == null) prefix = ""; // TODO } else { prefix = commenter.getLineCommentPrefix(); if (prefix == null) prefix = commenter.getBlockCommentPrefix(); } int lineStart = myDocument.getLineStartOffset(myStartLine); lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t"); lineStart += prefix.length(); lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t"); if (lineStart > myDocument.getTextLength()) lineStart = myDocument.getTextLength(); myEditor.getCaretModel().moveToOffset(lineStart); myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } } else { if (!hasSelection) { // Don't tweak caret position if we're already located on the last document line. LogicalPosition position = myEditor.getCaretModel().getLogicalPosition(); if (position.line < myDocument.getLineCount() - 1) { int verticalShift = 1 + myEditor.getSoftWrapModel().getSoftWrapsForLine(position.line).size() - position.softWrapLinesOnCurrentLogicalLine; myEditor.getCaretModel().moveCaretRelatively(0, verticalShift, false, false, true); } } else { if (wholeLinesSelected) { selectionModel.setSelection(myStartOffset, selectionModel.getSelectionEnd()); } } } }