diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index b6305bb2949c..d5131798e8ef 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -19,10 +19,12 @@ import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import com.intellij.openapi.editor.impl.softwrap.SoftWrapHelper; +import com.intellij.openapi.editor.impl.view.EditorPainter; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.DocumentUtil; import com.intellij.util.containers.ContainerUtil; @@ -509,16 +511,19 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { final EditorComponentImpl content = myEditor.getContentComponent(); int updateWidth = myEditor.getScrollPane().getHorizontalScrollBar().getValue() + visibleArea.width; + int additionalRepaintHeight = this == myEditor.getCaretModel().getPrimaryCaret() && Registry.is("editor.adjust.right.margin") && EditorPainter.isMarginShown(myEditor) ? 1 : 0; if (Math.abs(myCaretInfo.y - oldCaretInfo.y) <= 2 * lineHeight) { int minY = Math.min(oldCaretInfo.y, myCaretInfo.y); int maxY = Math.max(oldCaretInfo.y + oldCaretInfo.height, myCaretInfo.y + myCaretInfo.height); - content.repaintEditorComponent(0, minY, updateWidth, maxY - minY); + content.repaintEditorComponent(0, minY - additionalRepaintHeight, updateWidth, maxY - minY + additionalRepaintHeight); gutter.repaint(0, minY, gutter.getWidth(), maxY - minY); } else { - content.repaintEditorComponent(0, oldCaretInfo.y, updateWidth, oldCaretInfo.height + lineHeight); + content.repaintEditorComponent(0, oldCaretInfo.y - additionalRepaintHeight, + updateWidth, oldCaretInfo.height + lineHeight + additionalRepaintHeight); gutter.repaint(0, oldCaretInfo.y, updateWidth, oldCaretInfo.height + lineHeight); - content.repaintEditorComponent(0, myCaretInfo.y, updateWidth, myCaretInfo.height + lineHeight); + content.repaintEditorComponent(0, myCaretInfo.y - additionalRepaintHeight, + updateWidth, myCaretInfo.height + lineHeight + additionalRepaintHeight); gutter.repaint(0, myCaretInfo.y, updateWidth, myCaretInfo.height + lineHeight); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java index e356e2a09946..8faef42d46b5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java @@ -13,7 +13,6 @@ import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.impl.IdeBackgroundUtil; @@ -40,7 +39,7 @@ import java.util.Map; /** * Renders editor contents. */ -class EditorPainter implements TextDrawingCallback { +public class EditorPainter implements TextDrawingCallback { private static final Color CARET_LIGHT = Gray._255; private static final Color CARET_DARK = Gray._0; private static final Stroke IME_COMPOSED_TEXT_UNDERLINE_STROKE = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, @@ -87,8 +86,8 @@ class EditorPainter implements TextDrawingCallback { int yShift = -clip.y; g.translate(0, -yShift); - float[] marginShifts = paintBackground(g, clip, yShift, startLine, endLine, caretData); - paintRightMargin(g, clip, yShift, startLine, endLine, marginShifts); + float[] marginWidths = paintBackground(g, clip, yShift, startLine, endLine, caretData); + paintRightMargin(g, clip, yShift, startLine, endLine, marginWidths); paintCustomRenderers(g, yShift, startOffset, endOffset); MarkupModelEx docMarkup = myEditor.getFilteredDocumentMarkupModel(); paintLineMarkersSeparators(g, clip, yShift, docMarkup, startOffset, endOffset); @@ -143,19 +142,24 @@ class EditorPainter implements TextDrawingCallback { int yShift, int startVisualLine, int endVisualLine, - float[] marginShifts) { + float[] marginWidths) { if (!isMarginShown()) return; g.setColor(myEditor.getColorsScheme().getColor(EditorColors.RIGHT_MARGIN_COLOR)); - if (marginShifts == null) { - int x = myCorrector.marginX(); + float baseMarginWidth = getBaseMarginWidth(myView); + if (marginWidths == null) { + int x = myCorrector.marginX(baseMarginWidth); UIUtil.drawLine(g, x, 0, x, clip.height); } else { int y = myView.visualLineToY(startVisualLine) + yShift; int lineHeight = myView.getLineHeight(); for(int i = startVisualLine; i <= endVisualLine; i++) { - int x = myCorrector.marginX(marginShifts[i - startVisualLine]); - int nextX = myCorrector.marginX(marginShifts[i - startVisualLine + 1]); + float width = marginWidths[i - startVisualLine]; + if (width == 0) width = baseMarginWidth; + float nextWidth = marginWidths[i - startVisualLine + 1]; + if (nextWidth == 0) nextWidth = baseMarginWidth; + int x = myCorrector.marginX(width); + int nextX = myCorrector.marginX(nextWidth); g.fillRect(x, y, 1, lineHeight); if (nextX != x) g.fillRect(Math.min(x, nextX), y + lineHeight - 1, Math.abs(x - nextX) + 1, 1); y += lineHeight; @@ -170,11 +174,16 @@ class EditorPainter implements TextDrawingCallback { } } + private static float getBaseMarginWidth(EditorView view) { + Editor editor = view.getEditor(); + return editor.getSettings().getRightMargin(editor.getProject()) * view.getPlainSpaceWidth(); + } + private boolean isMarginShown() { return isMarginShown(myEditor); } - private static boolean isMarginShown(@NotNull Editor editor) { + public static boolean isMarginShown(@NotNull Editor editor) { return editor.getSettings().isRightMarginShown() && editor.getColorsScheme().getColor(EditorColors.RIGHT_MARGIN_COLOR) != null && (Registry.is("editor.show.right.margin.in.read.only.files") || editor.getDocument().isWritable()); @@ -182,9 +191,9 @@ class EditorPainter implements TextDrawingCallback { private float[] paintBackground(Graphics2D g, Rectangle clip, int yShift, int startVisualLine, int endVisualLine, IterationState.CaretData caretData) { - Ref marginShifts = new Ref<>(); - boolean calculateMarginShift = Registry.is("editor.adjust.right.margin") && isMarginShown(); - int maxVisualLine = endVisualLine + (calculateMarginShift ? 1 : 0); + boolean calculateMarginWidths = Registry.is("editor.adjust.right.margin") && isMarginShown(); + float[] marginWidths = calculateMarginWidths ? new float[endVisualLine - startVisualLine + 2] : null; + int maxVisualLine = endVisualLine + (calculateMarginWidths ? 1 : 0); int lineCount = myEditor.getVisibleLineCount(); final Map> virtualSelectionMap = createVirtualSelectionMap(startVisualLine, endVisualLine); @@ -240,13 +249,11 @@ class EditorPainter implements TextDrawingCallback { primarySelectionStart, primarySelectionEnd); } } - }, calculateMarginShift ? shift -> { - if (marginShifts.isNull()) marginShifts.set(new float[endVisualLine - startVisualLine + 2]); - marginShifts.get()[visualLine - startVisualLine] += shift; - } : null); + }, calculateMarginWidths && !visLinesIterator.endsWithSoftWrap() && !visLinesIterator.startsWithSoftWrap() + ? width -> marginWidths[visualLine - startVisualLine] = width : null); visLinesIterator.advance(); } - return marginShifts.get(); + return marginWidths; } private void paintFoldingBackground(Graphics2D g, TextAttributes attributes, float x, int y, float width, FoldRegion foldRegion) { @@ -1017,12 +1024,12 @@ class EditorPainter implements TextDrawingCallback { } } - private interface MarginShiftConsumer { - void process(float shift); + private interface MarginWidthConsumer { + void process(float width); } private void paintLineFragments(Graphics2D g, Rectangle clip, VisualLinesIterator visLineIterator, IterationState.CaretData caretData, - int y, LineFragmentPainter painter, MarginShiftConsumer marginShiftConsumer) { + int y, LineFragmentPainter painter, MarginWidthConsumer marginWidthConsumer) { int visualLine = visLineIterator.getVisualLine(); float x = myCorrector.startX(visualLine) + (visualLine == 0 ? myView.getPrefixTextWidthInPixels() : 0); int offset = visLineIterator.getVisualLineStartOffset(); @@ -1031,6 +1038,9 @@ class EditorPainter implements TextDrawingCallback { int prevEndOffset = -1; boolean firstFragment = true; int maxColumn = 0; + int marginColumns = myEditor.getSettings().getRightMargin(myEditor.getProject()); + int endLogicalLine = visLineIterator.getEndLogicalLine(); + boolean marginReached = false; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visLineIterator, null, true)) { int fragmentStartOffset = fragment.getStartOffset(); int start = fragmentStartOffset; @@ -1054,11 +1064,10 @@ class EditorPainter implements TextDrawingCallback { } } } - float marginShift = 0; FoldRegion foldRegion = fragment.getCurrentFoldRegion(); if (foldRegion == null) { if (start != prevEndOffset) { - it = new IterationState(myEditor, start, fragment.isRtl() ? offset : visualLineEndOffset, + it = new IterationState(myEditor, start, fragment.isRtl() ? offset : visualLineEndOffset, caretData, false, false, false, fragment.isRtl()); } prevEndOffset = end; @@ -1072,7 +1081,6 @@ class EditorPainter implements TextDrawingCallback { if (xNew >= clip.getMinX()) { painter.paint(g, fragment, 0, 0, attributes, x, xNew, y); } - if (marginShiftConsumer != null) marginShift = xNew - x; x = xNew; } else { @@ -1093,6 +1101,11 @@ class EditorPainter implements TextDrawingCallback { x = xNew; start = curEnd; } + if (marginWidthConsumer != null && fragment.getEndLogicalLine() == endLogicalLine && + fragment.getStartLogicalColumn() <= marginColumns && fragment.getEndLogicalColumn() > marginColumns) { + marginWidthConsumer.process(fragment.visualColumnToX(fragment.logicalToVisualColumn(marginColumns))); + marginReached = true; + } } } else { @@ -1103,10 +1116,7 @@ class EditorPainter implements TextDrawingCallback { x = xNew; prevEndOffset = -1; it = null; - if (marginShiftConsumer != null) marginShift = x - myCorrector.startX(visualLine) - - fragment.getEndLogicalColumn() * myView.getPlainSpaceWidth(); } - if (marginShift != 0) marginShiftConsumer.process(marginShift); if (x > clip.getMaxX()) return; maxColumn = fragment.getEndVisualColumn(); } @@ -1118,8 +1128,8 @@ class EditorPainter implements TextDrawingCallback { painter.paintBeforeLineStart(g, it.getBeforeLineStartBackgroundAttributes(), false, maxColumn, x, y); } if (it == null || it.getEndOffset() != visualLineEndOffset) { - it = new IterationState(myEditor, visualLineEndOffset == offset ? visualLineEndOffset - : DocumentUtil.getPreviousCodePointOffset(myDocument, visualLineEndOffset), + it = new IterationState(myEditor, visualLineEndOffset == offset + ? visualLineEndOffset : DocumentUtil.getPreviousCodePointOffset(myDocument, visualLineEndOffset), visualLineEndOffset, caretData, false, false, false, false); } if (!it.atEnd()) { @@ -1127,6 +1137,13 @@ class EditorPainter implements TextDrawingCallback { } assert it.atEnd(); painter.paintAfterLineEnd(g, clip, it, maxColumn, x, y); + if (marginWidthConsumer != null && !marginReached && + (visualLine == myEditor.getCaretModel().getVisualPosition().line || x > marginColumns * myView.getPlainSpaceWidth())) { + int endLogicalColumn = myView.offsetToLogicalPosition(visualLineEndOffset).column; + if (endLogicalColumn < marginColumns) { + marginWidthConsumer.process(x + (marginColumns - endLogicalColumn) * myView.getPlainSpaceWidth()); + } + } } private TextAttributes getFoldRegionAttributes(FoldRegion foldRegion) { @@ -1225,8 +1242,7 @@ class EditorPainter implements TextDrawingCallback { int lineSeparatorEnd(int maxX); float singleLineBorderStart(float x); float singleLineBorderEnd(float x); - int marginX(); - int marginX(float shift); + int marginX(float marginWidth); List softMarginsX(); @NotNull @@ -1289,18 +1305,12 @@ class EditorPainter implements TextDrawingCallback { @Override public int lineSeparatorEnd(int maxX) { - return isMarginShown(myView.getEditor()) ? Math.min(marginX(), maxX) : maxX; + return isMarginShown(myView.getEditor()) ? Math.min(marginX(getBaseMarginWidth(myView)), maxX) : maxX; } @Override - public int marginX() { - EditorImpl editor = myView.getEditor(); - return (int) (myView.getInsets().left + editor.getSettings().getRightMargin(editor.getProject()) * myView.getPlainSpaceWidth()); - } - - @Override - public int marginX(float shift) { - return marginX() + (int)shift; + public int marginX(float marginWidth) { + return (int)(myView.getInsets().left + marginWidth); } @Override @@ -1358,7 +1368,7 @@ class EditorPainter implements TextDrawingCallback { @Override public int lineSeparatorStart(int minX) { - return isMarginShown(myView.getEditor()) ? Math.max(marginX(), minX) : minX; + return isMarginShown(myView.getEditor()) ? Math.max(marginX(getBaseMarginWidth(myView)), minX) : minX; } @Override @@ -1367,15 +1377,8 @@ class EditorPainter implements TextDrawingCallback { } @Override - public int marginX() { - EditorImpl editor = myView.getEditor(); - return (int) (myView.getRightAlignmentMarginX() - - editor.getSettings().getRightMargin(editor.getProject()) * myView.getPlainSpaceWidth()); - } - - @Override - public int marginX(float shift) { - return marginX() - (int)shift; + public int marginX(float marginWidth) { + return (int)(myView.getRightAlignmentMarginX() - marginWidth); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java index ae850dce165a..20e0cea6b574 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLinesIterator.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.editor.impl.view; import com.intellij.openapi.editor.Document; @@ -59,6 +45,13 @@ public class VisualLinesIterator { } } + private void setNextLocation() { + if (myNextLocation == null) { + myNextLocation = myLocation.clone(); + myNextLocation.advance(); + } + } + public int getVisualLine() { checkEnd(); return myLocation.visualLine; @@ -68,23 +61,27 @@ public class VisualLinesIterator { checkEnd(); return myLocation.offset; } - + public int getVisualLineEndOffset() { checkEnd(); - if (myNextLocation == null) { - myNextLocation = myLocation.clone(); - myNextLocation.advance(); - } - return myNextLocation.atEnd() ? myDocument.getTextLength() : - myNextLocation.softWrap == myLocation.softWrap ? myDocument.getLineEndOffset(myNextLocation.logicalLine - 2) : + setNextLocation(); + return myNextLocation.atEnd() ? myDocument.getTextLength() : + myNextLocation.softWrap == myLocation.softWrap ? myDocument.getLineEndOffset(myNextLocation.logicalLine - 2) : myNextLocation.offset; } - + public int getStartLogicalLine() { checkEnd(); return myLocation.logicalLine - 1; - } - + } + + public int getEndLogicalLine() { + checkEnd(); + setNextLocation(); + return myNextLocation.atEnd() ? myDocument.getLineCount() - 1 + : myNextLocation.logicalLine - (myNextLocation.softWrap == myLocation.softWrap ? 2 : 1); + } + public int getStartOrPrevWrapIndex() { checkEnd(); return myLocation.softWrap - 1; @@ -100,6 +97,17 @@ public class VisualLinesIterator { return myLocation.y; } + public boolean startsWithSoftWrap() { + checkEnd(); + return myLocation.softWrap > 0 && myLocation.softWrap <= mySoftWraps.size() && + mySoftWraps.get(myLocation.softWrap - 1).getStart() == myLocation.offset; + } + + public boolean endsWithSoftWrap() { + checkEnd(); + return myLocation.softWrap < mySoftWraps.size() && mySoftWraps.get(myLocation.softWrap).getStart() == getVisualLineEndOffset(); + } + private void checkEnd() { if (atEnd()) throw new IllegalStateException("Iteration finished"); }