private int wrapPositionForTabbedTextWithOptimization( @NotNull CharSequence text, int tabSize, int startLineOffset, int endLineOffset, int targetRangeEndOffset) { int width = 0; int symbolWidth; int result = Integer.MAX_VALUE; boolean wrapLine = false; for (int i = startLineOffset; i < Math.min(endLineOffset, targetRangeEndOffset); i++) { char c = text.charAt(i); switch (c) { case '\t': symbolWidth = tabSize - (width % tabSize); break; default: symbolWidth = 1; } if (width + symbolWidth + FormatConstants.RESERVED_LINE_WRAP_WIDTH_IN_COLUMNS >= mySettings.RIGHT_MARGIN && (Math.min(endLineOffset, targetRangeEndOffset) - i) >= FormatConstants.RESERVED_LINE_WRAP_WIDTH_IN_COLUMNS) { // Remember preferred position. result = i - 1; } if (width + symbolWidth >= mySettings.RIGHT_MARGIN) { wrapLine = true; break; } width += symbolWidth; } return wrapLine ? result : -1; }
private int wrapPositionForTextWithoutTabs( int startLineOffset, int endLineOffset, int targetRangeEndOffset) { if (Math.min(endLineOffset, targetRangeEndOffset) - startLineOffset > mySettings.RIGHT_MARGIN) { return startLineOffset + mySettings.RIGHT_MARGIN - FormatConstants.RESERVED_LINE_WRAP_WIDTH_IN_COLUMNS; } return -1; }
private int wrapPositionForTabbedTextWithoutOptimization( @NotNull Editor editor, @NotNull CharSequence text, int spaceSize, int startLineOffset, int endLineOffset, int targetRangeEndOffset) { int width = 0; int x = 0; int newX; int symbolWidth; int result = Integer.MAX_VALUE; boolean wrapLine = false; for (int i = startLineOffset; i < Math.min(endLineOffset, targetRangeEndOffset); i++) { char c = text.charAt(i); switch (c) { case '\t': newX = EditorUtil.nextTabStop(x, editor); int diffInPixels = newX - x; symbolWidth = diffInPixels / spaceSize; if (diffInPixels % spaceSize > 0) { symbolWidth++; } break; default: newX = x + EditorUtil.charWidth(c, Font.PLAIN, editor); symbolWidth = 1; } if (width + symbolWidth + FormatConstants.RESERVED_LINE_WRAP_WIDTH_IN_COLUMNS >= mySettings.RIGHT_MARGIN && (Math.min(endLineOffset, targetRangeEndOffset) - i) >= FormatConstants.RESERVED_LINE_WRAP_WIDTH_IN_COLUMNS) { result = i - 1; } if (width + symbolWidth >= mySettings.RIGHT_MARGIN) { wrapLine = true; break; } x = newX; width += symbolWidth; } return wrapLine ? result : -1; }
/** * Checks if it's worth to try to wrap target line (it's long enough) and tries to calculate * preferred wrap position. * * @param editor target editor * @param text text contained at the given editor * @param tabSize tab space to use (number of visual columns occupied by a tab) * @param spaceSize space width in pixels * @param startLineOffset start offset of the text line to process * @param endLineOffset end offset of the text line to process * @param targetRangeEndOffset target text region's end offset * @return negative value if no wrapping should be performed for the target line; preferred wrap * position otherwise */ private int calculatePreferredWrapPosition( @NotNull Editor editor, @NotNull CharSequence text, int tabSize, int spaceSize, int startLineOffset, int endLineOffset, int targetRangeEndOffset) { boolean hasTabs = false; boolean canOptimize = true; boolean hasNonSpaceSymbols = false; loop: for (int i = startLineOffset; i < Math.min(endLineOffset, targetRangeEndOffset); i++) { char c = text.charAt(i); switch (c) { case '\t': { hasTabs = true; if (hasNonSpaceSymbols) { canOptimize = false; break loop; } } case ' ': break; default: hasNonSpaceSymbols = true; } } if (!hasTabs) { return wrapPositionForTextWithoutTabs(startLineOffset, endLineOffset, targetRangeEndOffset); } else if (canOptimize) { return wrapPositionForTabbedTextWithOptimization( text, tabSize, startLineOffset, endLineOffset, targetRangeEndOffset); } else { return wrapPositionForTabbedTextWithoutOptimization( editor, text, spaceSize, startLineOffset, endLineOffset, targetRangeEndOffset); } }
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]; } }