mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
remove registry option editor.new.rendering and code supporting its legacy value
This commit is contained in:
@@ -81,9 +81,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
|
||||
|
||||
private final List<EditReadOnlyListener> myReadOnlyListeners = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
|
||||
private volatile boolean myMightContainTabs = true; // optimisation flag: when document contains no tabs it is dramatically easier to calculate positions in editor
|
||||
private int myTabTrackingRequestors;
|
||||
|
||||
private int myCheckGuardedBlocks;
|
||||
private boolean myGuardsSuppressed;
|
||||
private boolean myEventsHandling;
|
||||
@@ -783,9 +780,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
|
||||
assert getTextLength() == myLineSet.getLength() : "getTextLength() = " + getTextLength()+ "; myLineSet.getLength() = " + myLineSet.getLength();
|
||||
|
||||
myFrozen = null;
|
||||
if (myTabTrackingRequestors > 0) {
|
||||
updateMightContainTabs(event.getNewFragment());
|
||||
}
|
||||
setModificationStamp(newModificationStamp);
|
||||
|
||||
if (!ShutDownTracker.isShutdownHookRunning()) {
|
||||
@@ -1125,35 +1119,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
|
||||
return "DocumentImpl[" + FileDocumentManager.getInstance().getFile(this) + "]";
|
||||
}
|
||||
|
||||
void requestTabTracking() {
|
||||
if (myAssertThreading) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
}
|
||||
if (myTabTrackingRequestors++ == 0) {
|
||||
myMightContainTabs = false;
|
||||
updateMightContainTabs(myText);
|
||||
}
|
||||
}
|
||||
|
||||
void giveUpTabTracking() {
|
||||
if (myAssertThreading) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
}
|
||||
if (--myTabTrackingRequestors == 0) {
|
||||
myMightContainTabs = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean mightContainTabs() {
|
||||
return myMightContainTabs;
|
||||
}
|
||||
|
||||
private void updateMightContainTabs(CharSequence text) {
|
||||
if (!myMightContainTabs) {
|
||||
myMightContainTabs = StringUtil.contains(text, 0, text.length(), '\t');
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FrozenDocument freeze() {
|
||||
FrozenDocument frozen = myFrozen;
|
||||
|
||||
@@ -116,7 +116,7 @@ public class EditorWindowImpl extends UserDataHolderBase implements EditorWindow
|
||||
myMarkupModelDelegate = new MarkupModelWindow(myDelegate.getMarkupModel(), myDocumentWindow);
|
||||
myDocumentMarkupModelDelegate = new MarkupModelWindow(myDelegate.getFilteredDocumentMarkupModel(), myDocumentWindow);
|
||||
myFoldingModelWindow = new FoldingModelWindow(delegate.getFoldingModel(), documentWindow, this);
|
||||
mySoftWrapModel = new SoftWrapModelWindow(this);
|
||||
mySoftWrapModel = new SoftWrapModelWindow();
|
||||
myInlayModel = new InlayModelWindow();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.intellij.injected.editor;
|
||||
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SoftWrap;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.ex.SoftWrapChangeListener;
|
||||
@@ -30,34 +29,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class SoftWrapModelWindow implements SoftWrapModelEx {
|
||||
private final EditorWindowImpl myEditorWindow;
|
||||
|
||||
SoftWrapModelWindow(EditorWindowImpl editorWindow) {
|
||||
myEditorWindow = editorWindow;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual) {
|
||||
return myEditorWindow.visualToLogicalPosition(visual);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
return myEditorWindow.offsetToLogicalPosition(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int logicalPositionToOffset(@NotNull LogicalPosition logicalPosition) {
|
||||
return myEditorWindow.logicalPositionToOffset(logicalPosition);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual) {
|
||||
return defaultVisual;
|
||||
}
|
||||
SoftWrapModelWindow() {}
|
||||
|
||||
@Override
|
||||
public List<? extends SoftWrap> getRegisteredSoftWraps() {
|
||||
|
||||
+5
-4
@@ -25,13 +25,14 @@ import com.intellij.codeInsight.folding.CodeFoldingManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorState;
|
||||
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.util.Producer;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -149,7 +150,7 @@ public class PsiAwareTextEditorProvider extends TextEditorProvider {
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return !Registry.is("editor.new.rendering") || !getEditor().isDisposed();
|
||||
return !getEditor().isDisposed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ public class EditorFragmentComponent extends JPanel {
|
||||
private void doInit(EditorEx editor, int startLine, int endLine, boolean showFolding, boolean showGutter) {
|
||||
Document doc = editor.getDocument();
|
||||
final int endOffset = endLine < doc.getLineCount() ? doc.getLineEndOffset(endLine) : doc.getTextLength();
|
||||
boolean newRendering = editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering;
|
||||
boolean newRendering = editor instanceof EditorImpl;
|
||||
int widthAdjustment = newRendering ? EditorUtil.getSpaceWidth(Font.PLAIN, editor) : 0;
|
||||
final int textImageWidth = Math.min(
|
||||
editor.getMaxWidthInRange(doc.getLineStartOffset(startLine), endOffset) + widthAdjustment,
|
||||
|
||||
@@ -284,7 +284,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap
|
||||
if (editor == null) {
|
||||
return null;
|
||||
}
|
||||
if (Registry.is("editor.new.rendering") && !editor.isValid()) {
|
||||
if (!editor.isValid()) {
|
||||
return null;
|
||||
}
|
||||
return new EditorAndState(editor, editor.getState(FileEditorStateLevel.UNDO));
|
||||
|
||||
+3
-3
@@ -451,7 +451,7 @@ public class EditorActionUtil {
|
||||
int logLineEndOffset = document.getLineEndOffset(logLine);
|
||||
LogicalPosition logLineStart = editor.offsetToLogicalPosition(logLineStartOffset);
|
||||
VisualPosition visLineStart = editor.logicalToVisualPosition(logLineStart);
|
||||
boolean newRendering = editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering;
|
||||
boolean newRendering = editor instanceof EditorImpl;
|
||||
|
||||
boolean softWrapIntroducedLine = visLineStart.line != visualLineNumber;
|
||||
if (!softWrapIntroducedLine) {
|
||||
@@ -625,7 +625,7 @@ public class EditorActionUtil {
|
||||
caretModel.moveToVisualPosition(visualEndOfLineWithCaret);
|
||||
}
|
||||
else {
|
||||
if (editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) {
|
||||
if (editor instanceof EditorImpl) {
|
||||
caretModel.moveToLogicalPosition(editor.offsetToLogicalPosition(newOffset).leanForward(true));
|
||||
}
|
||||
else {
|
||||
@@ -765,7 +765,7 @@ public class EditorActionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) {
|
||||
if (editor instanceof EditorImpl) {
|
||||
int boundaryOffset = ((EditorImpl)editor).findNearestDirectionBoundary(offset, false);
|
||||
if (boundaryOffset >= 0) {
|
||||
newOffset = Math.max(boundaryOffset, newOffset);
|
||||
|
||||
+3
-15
@@ -24,7 +24,6 @@ import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
|
||||
class MoveCaretLeftOrRightHandler extends EditorActionHandler {
|
||||
enum Direction {LEFT, RIGHT}
|
||||
@@ -49,22 +48,11 @@ class MoveCaretLeftOrRightHandler extends EditorActionHandler {
|
||||
|
||||
if (start <= caretOffset && end >= caretOffset) { // See IDEADEV-36957
|
||||
|
||||
VisualPosition targetPosition = null;
|
||||
if (Registry.is("editor.new.rendering")) {
|
||||
targetPosition = myDirection == Direction.RIGHT ? caret.getSelectionEndPosition() : caret.getSelectionStartPosition();
|
||||
}
|
||||
else if (caretModel.supportsMultipleCarets() && editor.isColumnMode()) {
|
||||
targetPosition = myDirection == Direction.RIGHT ?
|
||||
selectionModel.getSelectionEndPosition() : selectionModel.getSelectionStartPosition();
|
||||
}
|
||||
VisualPosition targetPosition = myDirection == Direction.RIGHT ? caret.getSelectionEndPosition()
|
||||
: caret.getSelectionStartPosition();
|
||||
|
||||
selectionModel.removeSelection();
|
||||
if (targetPosition != null) {
|
||||
caretModel.moveToVisualPosition(targetPosition);
|
||||
}
|
||||
else {
|
||||
caretModel.moveToOffset(myDirection == Direction.RIGHT ^ caret.isAtRtlLocation() ? end : start);
|
||||
}
|
||||
caretModel.moveToVisualPosition(targetPosition);
|
||||
if (caret == editor.getCaretModel().getPrimaryCaret()) {
|
||||
scrollingModel.scrollToCaret(ScrollType.RELATIVE);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class TextEndAction extends TextComponentEditorAction {
|
||||
public void execute(Editor editor, DataContext dataContext) {
|
||||
editor.getCaretModel().removeSecondaryCarets();
|
||||
int offset = editor.getDocument().getTextLength();
|
||||
if (editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) {
|
||||
if (editor instanceof EditorImpl) {
|
||||
editor.getCaretModel().moveToLogicalPosition(editor.offsetToLogicalPosition(offset).leanForward(true));
|
||||
}
|
||||
else {
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ public class TextEndWithSelectionAction extends TextComponentEditorAction {
|
||||
caret = carets.get(0);
|
||||
}
|
||||
int selectionStart = caret.getLeadSelectionOffset();
|
||||
if (editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) {
|
||||
if (editor instanceof EditorImpl) {
|
||||
caret.moveToLogicalPosition(editor.offsetToLogicalPosition(endOffset).leanForward(true));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -32,44 +32,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface SoftWrapModelEx extends SoftWrapModel {
|
||||
|
||||
/**
|
||||
* Asks current model to map given visual position to logical.
|
||||
*
|
||||
* @param visual target visual position for which logical position should be mapped
|
||||
* @return logical position that corresponds to the given visual position
|
||||
*/
|
||||
@NotNull
|
||||
LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual);
|
||||
|
||||
/**
|
||||
* Asks current model to map given document offset to logical position.
|
||||
*
|
||||
* @param offset target editor document offset
|
||||
* @return logical position for the given editor document offset
|
||||
*/
|
||||
@NotNull
|
||||
LogicalPosition offsetToLogicalPosition(int offset);
|
||||
|
||||
/**
|
||||
* Asks current model to map given logical position to document offset
|
||||
*
|
||||
* @param logicalPosition target editor logical position
|
||||
* @return document offset for the given editor logical position
|
||||
*/
|
||||
int logicalPositionToOffset(@NotNull LogicalPosition logicalPosition);
|
||||
|
||||
/**
|
||||
* Asks current model to adjust visual position that corresponds to the given logical position if necessary.
|
||||
* <p/>
|
||||
* Given visual position is assumed to be the one that is obtained during soft wraps unaware processing.
|
||||
*
|
||||
* @param logical target logical position for {@code 'logical' -> visual} conversion
|
||||
* @param defaultVisual visual position of {@code 'logical' -> visual} conversion that is unaware about soft wraps
|
||||
* @return resulting visual position for the given logical position
|
||||
*/
|
||||
@NotNull
|
||||
VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual);
|
||||
|
||||
/**
|
||||
* @return unmodifiable collection of soft wraps currently registered within the current model
|
||||
*/
|
||||
|
||||
@@ -76,7 +76,7 @@ public final class EditorUtil {
|
||||
}
|
||||
|
||||
public static int getLastVisualLineColumnNumber(@NotNull Editor editor, final int line) {
|
||||
if (editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) {
|
||||
if (editor instanceof EditorImpl) {
|
||||
LogicalPosition lineEndPosition = editor.visualToLogicalPosition(new VisualPosition(line, Integer.MAX_VALUE));
|
||||
int lineEndOffset = editor.logicalPositionToOffset(lineEndPosition);
|
||||
return editor.offsetToVisualPosition(lineEndOffset, true, true).column;
|
||||
@@ -171,15 +171,9 @@ public final class EditorUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
CharSequence editorInfo;
|
||||
if (editor instanceof EditorImpl) {
|
||||
editorInfo = ((EditorImpl)editor).dumpState();
|
||||
}
|
||||
else {
|
||||
editorInfo = "editor's class: " + editor.getClass()
|
||||
+ ", all soft wraps: " + editor.getSoftWrapModel().getSoftWrapsForRange(0, document.getTextLength())
|
||||
+ ", fold regions: " + Arrays.toString(editor.getFoldingModel().getAllFoldRegions());
|
||||
}
|
||||
CharSequence editorInfo = "editor's class: " + editor.getClass()
|
||||
+ ", all soft wraps: " + editor.getSoftWrapModel().getSoftWrapsForRange(0, document.getTextLength())
|
||||
+ ", fold regions: " + Arrays.toString(editor.getFoldingModel().getAllFoldRegions());
|
||||
LogMessageEx.error(LOG, "Can't calculate last visual column", String.format(
|
||||
"Target visual line: %d, mapped logical line: %d, visual lines range for the mapped logical line: [%s]-[%s], soft wraps for "
|
||||
+ "the target logical line: %s. Editor info: %s",
|
||||
@@ -279,31 +273,24 @@ public final class EditorUtil {
|
||||
// if tab size is four and current column is one etc. So, first of all we check if there are tabulation symbols at the target
|
||||
// text fragment.
|
||||
boolean useOptimization = true;
|
||||
boolean hasTabs;
|
||||
if (editor instanceof EditorImpl && !((EditorImpl)editor).hasTabs()) {
|
||||
hasTabs = false;
|
||||
useOptimization = true;
|
||||
}
|
||||
else {
|
||||
hasTabs = false;
|
||||
int scanEndOffset = Math.min(end, start + columnNumber - currentColumn[0] + 1);
|
||||
boolean hasNonTabs = false;
|
||||
for (int i = start; i < scanEndOffset; i++) {
|
||||
char c = text.charAt(i);
|
||||
if (debugBuffer != null) {
|
||||
debugBuffer.append(String.format("Found symbol '%c' at the offset %d%n", c, i));
|
||||
}
|
||||
if (c == '\t') {
|
||||
hasTabs = true;
|
||||
if (hasNonTabs) {
|
||||
useOptimization = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasNonTabs = true;
|
||||
boolean hasTabs = false;
|
||||
int scanEndOffset = Math.min(end, start + columnNumber - currentColumn[0] + 1);
|
||||
boolean hasNonTabs = false;
|
||||
for (int i = start; i < scanEndOffset; i++) {
|
||||
char c = text.charAt(i);
|
||||
if (debugBuffer != null) {
|
||||
debugBuffer.append(String.format("Found symbol '%c' at the offset %d%n", c, i));
|
||||
}
|
||||
if (c == '\t') {
|
||||
hasTabs = true;
|
||||
if (hasNonTabs) {
|
||||
useOptimization = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasNonTabs = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (debugBuffer != null) {
|
||||
@@ -437,24 +424,18 @@ public final class EditorUtil {
|
||||
SoftWrap softWrap = editor.getSoftWrapModel().getSoftWrap(start);
|
||||
useOptimization = softWrap == null;
|
||||
}
|
||||
boolean hasTabs = true;
|
||||
if (useOptimization) {
|
||||
if (editor instanceof EditorImpl && !((EditorImpl)editor).hasTabs()) {
|
||||
hasTabs = false;
|
||||
}
|
||||
else {
|
||||
boolean hasNonTabs = false;
|
||||
for (int i = start; i < offset; i++) {
|
||||
if (text.charAt(i) == '\t') {
|
||||
if (hasNonTabs) {
|
||||
useOptimization = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasNonTabs = true;
|
||||
boolean hasNonTabs = false;
|
||||
for (int i = start; i < offset; i++) {
|
||||
if (text.charAt(i) == '\t') {
|
||||
if (hasNonTabs) {
|
||||
useOptimization = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasNonTabs = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,12 +457,10 @@ public final class EditorUtil {
|
||||
}
|
||||
|
||||
int shift = 0;
|
||||
if (hasTabs) {
|
||||
for (int i = start; i < offset; i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\t') {
|
||||
shift += getTabLength(i + shift - start, tabSize) - 1;
|
||||
}
|
||||
for (int i = start; i < offset; i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\t') {
|
||||
shift += getTabLength(i + shift - start, tabSize) - 1;
|
||||
}
|
||||
}
|
||||
return offset - start + shift;
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.editor.ex.MarkupModelEx;
|
||||
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.markup.EffectType;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.Equality;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class BorderEffect {
|
||||
private final Graphics myGraphics;
|
||||
private final int myStartOffset;
|
||||
private final int myEndOffset;
|
||||
private final TextRange myRange;
|
||||
private final EditorImpl myEditor;
|
||||
private final ClipDetector myClipDetector;
|
||||
private static final Equality<TextAttributes> SAME_COLOR_BOXES = new Equality<TextAttributes>() {
|
||||
@Override
|
||||
public boolean equals(final TextAttributes attributes1, final TextAttributes attributes2) {
|
||||
Color effectColor = attributes1.getEffectColor();
|
||||
EffectType effectType = attributes1.getEffectType();
|
||||
return effectColor != null
|
||||
&& effectColor.equals(attributes2.getEffectColor())
|
||||
&& (EffectType.BOXED == effectType || EffectType.ROUNDED_BOX == effectType) &&
|
||||
effectType == attributes2.getEffectType();
|
||||
}
|
||||
};
|
||||
private static final Condition<TextAttributes> BOX_FILTER = attributes -> isBorder(attributes);
|
||||
|
||||
public BorderEffect(EditorImpl editor, Graphics graphics, int clipStartOffset, int clipEndOffset) {
|
||||
myEditor = editor;
|
||||
myGraphics = graphics;
|
||||
myStartOffset = clipStartOffset;
|
||||
myEndOffset = clipEndOffset;
|
||||
myRange = new TextRange(myStartOffset, myEndOffset);
|
||||
myClipDetector = new ClipDetector(editor, graphics.getClipBounds());
|
||||
}
|
||||
|
||||
private static boolean isBorder(TextAttributes textAttributes) {
|
||||
return textAttributes != null &&
|
||||
textAttributes.getEffectColor() != null &&
|
||||
(EffectType.BOXED == textAttributes.getEffectType() || EffectType.ROUNDED_BOX == textAttributes.getEffectType());
|
||||
}
|
||||
|
||||
private void paintBorder(RangeHighlighterEx rangeHighlighter, TextAttributes textAttributes) {
|
||||
paintBorder(textAttributes.getEffectColor(),
|
||||
rangeHighlighter.getAffectedAreaStartOffset(),
|
||||
rangeHighlighter.getAffectedAreaEndOffset(),
|
||||
textAttributes.getEffectType());
|
||||
}
|
||||
|
||||
private void paintBorder(Color color, int startOffset, int endOffset, EffectType effectType) {
|
||||
paintBorder(myGraphics, myEditor, startOffset, endOffset, color, effectType);
|
||||
}
|
||||
|
||||
public void paintHighlighters(MarkupModelEx markupModel) {
|
||||
markupModel.processRangeHighlightersOverlappingWith(myStartOffset, myEndOffset, rangeHighlighter -> {
|
||||
TextAttributes textAttributes = rangeHighlighter.getTextAttributes();
|
||||
if (isBorder(textAttributes)) {
|
||||
paintBorder(rangeHighlighter, textAttributes);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void paintHighlighters(EditorHighlighter highlighter) {
|
||||
int startOffset = startOfLineByOffset(myStartOffset);
|
||||
if (startOffset < 0 || startOffset >= myEditor.getDocument().getTextLength()) return;
|
||||
RangeIterator iterator = new RangeIterator(new FoldingOrNewLineGaps(myEditor), SAME_COLOR_BOXES,
|
||||
highlighter.createIterator(startOffset),
|
||||
BOX_FILTER);
|
||||
iterator.init(myRange);
|
||||
while (!iterator.atEnd()) {
|
||||
iterator.advance();
|
||||
paintBorder(myGraphics, myEditor, iterator.getStart(), iterator.getEnd(),
|
||||
iterator.getTextAttributes().getEffectColor(), iterator.getTextAttributes().getEffectType());
|
||||
}
|
||||
}
|
||||
|
||||
private int startOfLineByOffset(int offset) {
|
||||
int line = myEditor.offsetToLogicalLine(offset);
|
||||
if (line >= myEditor.getDocument().getLineCount()) return -1;
|
||||
return myEditor.getDocument().getLineStartOffset(line);
|
||||
}
|
||||
|
||||
private void paintBorder(Graphics g, EditorImpl editor, int startOffset, int endOffset, Color color, EffectType effectType) {
|
||||
Color savedColor = g.getColor();
|
||||
g.setColor(color);
|
||||
paintBorder(g, editor, startOffset, endOffset, effectType);
|
||||
g.setColor(savedColor);
|
||||
}
|
||||
|
||||
private void paintBorder(Graphics g, EditorImpl editor, int startOffset, int endOffset, EffectType effectType) {
|
||||
if (!myClipDetector.rangeCanBeVisible(startOffset, endOffset)) return;
|
||||
Point startPoint = offsetToXY(editor, startOffset);
|
||||
Point endPoint = offsetToXY(editor, endOffset);
|
||||
int height = endPoint.y - startPoint.y;
|
||||
int startX = startPoint.x;
|
||||
int startY = startPoint.y;
|
||||
int endX = endPoint.x;
|
||||
int lineHeight = editor.getLineHeight();
|
||||
if (height == 0) {
|
||||
int width = endX == startX ? 1 : endX - startX - 1;
|
||||
if (effectType == EffectType.ROUNDED_BOX) {
|
||||
UIUtil.drawRectPickedOut((Graphics2D)g, startX, startY, width, lineHeight - 1);
|
||||
} else {
|
||||
g.drawRect(startX, startY, width, lineHeight - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int startLine = editor.offsetToVisualLine(startOffset);
|
||||
int endLine = editor.offsetToVisualLine(endOffset);
|
||||
int maxWidth = Math.max(endX, editor.getMaxWidthInVisualLineRange(startLine, endLine - 1, false));
|
||||
BorderGraphics border = new BorderGraphics(g, startX, startY, effectType);
|
||||
border.horizontalTo(maxWidth);
|
||||
border.verticalRel(height - 1);
|
||||
border.horizontalTo(endX);
|
||||
if (endX > 0) {
|
||||
border.verticalRel(lineHeight);
|
||||
border.horizontalTo(0);
|
||||
border.verticalRel(-height + 1);
|
||||
}
|
||||
else if (height > lineHeight) {
|
||||
border.verticalRel(-height + lineHeight + 1);
|
||||
}
|
||||
border.horizontalTo(startX);
|
||||
border.verticalTo(startY);
|
||||
}
|
||||
|
||||
private static Point offsetToXY(EditorImpl editor, int offset) {
|
||||
return editor.logicalPositionToXY(editor.offsetToLogicalPosition(offset));
|
||||
}
|
||||
|
||||
public static void paintFoldedEffect(Graphics g, int foldingXStart,
|
||||
int y, int foldingXEnd, int lineHeight, Color effectColor,
|
||||
EffectType effectType) {
|
||||
if (effectColor == null || effectType != EffectType.BOXED) return;
|
||||
g.setColor(effectColor);
|
||||
g.drawRect(foldingXStart, y, foldingXEnd - foldingXStart, lineHeight - 1);
|
||||
}
|
||||
|
||||
private static class FoldingOrNewLineGaps implements RangeIterator.Gaps {
|
||||
private final RangeIterator.FoldingGaps myFoldingGaps;
|
||||
private final CharSequence myChars;
|
||||
|
||||
public FoldingOrNewLineGaps(CharSequence chars, RangeIterator.FoldingGaps foldingGaps) {
|
||||
myChars = chars;
|
||||
myFoldingGaps = foldingGaps;
|
||||
}
|
||||
|
||||
public FoldingOrNewLineGaps(EditorImpl editor) {
|
||||
this(editor.getDocument().getCharsSequence(), new RangeIterator.FoldingGaps(editor.getFoldingModel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGapAt(int offset) {
|
||||
return myChars.charAt(offset) == '\n' || myFoldingGaps.isGapAt(offset);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BorderGraphics {
|
||||
private final Graphics myGraphics;
|
||||
|
||||
private int myX;
|
||||
private int myY;
|
||||
private EffectType myEffectType;
|
||||
|
||||
public BorderGraphics(Graphics graphics, int startX, int stIntY, EffectType effectType) {
|
||||
myGraphics = graphics;
|
||||
|
||||
myX = startX;
|
||||
myY = stIntY;
|
||||
myEffectType = effectType;
|
||||
}
|
||||
|
||||
public void horizontalTo(int x) {
|
||||
lineTo(x, myY);
|
||||
}
|
||||
|
||||
public void horizontalRel(int width) {
|
||||
lineTo(myX + width, myY);
|
||||
}
|
||||
|
||||
private void lineTo(int x, int y) {
|
||||
if (myEffectType == EffectType.ROUNDED_BOX) {
|
||||
UIUtil.drawLinePickedOut(myGraphics, myX, myY, x, y);
|
||||
} else {
|
||||
UIUtil.drawLine(myGraphics, myX, myY, x, y);
|
||||
}
|
||||
myX = x;
|
||||
myY = y;
|
||||
}
|
||||
|
||||
public void verticalRel(int height) {
|
||||
lineTo(myX, myY + height);
|
||||
}
|
||||
|
||||
public void verticalTo(int y) {
|
||||
lineTo(myX, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable {
|
||||
if (!myEditor.getSoftWrapModel().isInsideSoftWrap(pos)) {
|
||||
LogicalPosition log = myEditor.visualToLogicalPosition(new VisualPosition(newLineNumber, newColumnNumber, newLeansRight));
|
||||
int offset = myEditor.logicalPositionToOffset(log);
|
||||
if (offset >= document.getTextLength() && (!myEditor.myUseNewRendering || columnShift == 0)) {
|
||||
if (offset >= document.getTextLength() && columnShift == 0) {
|
||||
int lastOffsetColumn = myEditor.offsetToVisualPosition(document.getTextLength(), true, false).column;
|
||||
// We want to move caret to the last column if if it's located at the last line and 'Down' is pressed.
|
||||
if (lastOffsetColumn > newColumnNumber) {
|
||||
@@ -508,8 +508,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable {
|
||||
}
|
||||
}
|
||||
|
||||
if (myEditor.myUseNewRendering ? !oldVisualPosition.equals(myVisibleCaret) :
|
||||
!oldCaretPosition.toVisualPosition().equals(myLogicalCaret.toVisualPosition())) {
|
||||
if (!oldVisualPosition.equals(myVisibleCaret)) {
|
||||
CaretEvent event = new CaretEvent(myEditor, this, oldCaretPosition, myLogicalCaret);
|
||||
if (fireListeners) {
|
||||
myEditor.getCaretModel().fireCaretPositionChanged(event);
|
||||
@@ -590,15 +589,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable {
|
||||
|
||||
if (!editorSettings.isVirtualSpace()) {
|
||||
int lineEndColumn = EditorUtil.getLastVisualLineColumnNumber(myEditor, line);
|
||||
if (column > lineEndColumn && (!myEditor.myUseNewRendering || !myEditor.getSoftWrapModel().isInsideSoftWrap(pos))) {
|
||||
if (column > lineEndColumn && !myEditor.getSoftWrapModel().isInsideSoftWrap(pos)) {
|
||||
column = lineEndColumn;
|
||||
leanRight = true;
|
||||
}
|
||||
|
||||
if (!myEditor.myUseNewRendering && column < 0 && line > 0) {
|
||||
line--;
|
||||
column = EditorUtil.getLastVisualLineColumnNumber(myEditor, line);
|
||||
}
|
||||
}
|
||||
|
||||
myVisibleCaret = new VisualPosition(line, column, leanRight);
|
||||
@@ -724,15 +718,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable {
|
||||
// There is a possible case that active logical line is represented on multiple lines due to soft wraps processing.
|
||||
// We want to highlight those visual lines as 'active' then, so, we calculate 'y' position for the logical line start
|
||||
// and height in accordance with the number of occupied visual lines.
|
||||
int y;
|
||||
if (myEditor.myUseNewRendering) {
|
||||
int visualLine = myEditor.offsetToVisualLine(document.getLineStartOffset(logicalLine));
|
||||
y = myEditor.visibleLineToY(visualLine);
|
||||
}
|
||||
else {
|
||||
VisualPosition visualPosition = myEditor.offsetToVisualPosition(document.getLineStartOffset(logicalLine));
|
||||
y = myEditor.visualPositionToXY(visualPosition).y;
|
||||
}
|
||||
int visualLine = myEditor.offsetToVisualLine(document.getLineStartOffset(logicalLine));
|
||||
int y = myEditor.visibleLineToY(visualLine);
|
||||
int lineHeight = myEditor.getLineHeight();
|
||||
int height = lineHeight;
|
||||
List<? extends SoftWrap> softWraps = myEditor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffset);
|
||||
@@ -1391,12 +1378,12 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable {
|
||||
|
||||
@Override
|
||||
public boolean isAtRtlLocation() {
|
||||
return myEditor.myUseNewRendering && myEditor.myView.isRtlLocation(getVisualPosition());
|
||||
return myEditor.myView.isRtlLocation(getVisualPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAtBidiRunBoundary() {
|
||||
return myEditor.myUseNewRendering && myEditor.myView.isAtBidiRunBoundary(getVisualPosition());
|
||||
return myEditor.myView.isAtBidiRunBoundary(getVisualPosition());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -236,17 +236,6 @@ public class EditorComponentImpl extends JTextComponent implements Scrollable, D
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revalidate() {
|
||||
// Null-check necessary because JTextView constructor invokes overridden method (updateUI, which calls revalidate)
|
||||
// before our own constructor has had a chance to run
|
||||
//noinspection ConstantConditions
|
||||
if (myEditor != null) {
|
||||
myEditor.resetPaintersWidth();
|
||||
}
|
||||
super.revalidate();
|
||||
}
|
||||
|
||||
public void repaintEditorComponent() {
|
||||
repaint();
|
||||
}
|
||||
|
||||
+6
-17
@@ -43,6 +43,7 @@ import com.intellij.openapi.editor.event.EditorMouseEventArea;
|
||||
import com.intellij.openapi.editor.ex.*;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUIUtil;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.impl.view.IterationState;
|
||||
import com.intellij.openapi.editor.impl.view.VisualLinesIterator;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
@@ -322,23 +323,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
Color defaultBackgroundColor = myEditor.getBackgroundColor();
|
||||
Color defaultForegroundColor = myEditor.getColorsScheme().getDefaultForeground();
|
||||
int startX = myEditor.isInDistractionFreeMode() ? 0 : getWhitespaceSeparatorOffset() + (isFoldingOutlineShown() ? 1 : 0);
|
||||
if (myEditor.myUseNewRendering) {
|
||||
com.intellij.openapi.editor.impl.view.IterationState state =
|
||||
new com.intellij.openapi.editor.impl.view.IterationState(myEditor, firstVisibleOffset, lastVisibleOffset,
|
||||
null, true, false, true, false);
|
||||
while (!state.atEnd()) {
|
||||
drawEditorBackgroundForRange(g, state.getStartOffset(), state.getEndOffset(), state.getMergedAttributes(),
|
||||
defaultBackgroundColor, defaultForegroundColor, startX);
|
||||
state.advance();
|
||||
}
|
||||
}
|
||||
else {
|
||||
IterationState state = new IterationState(myEditor, firstVisibleOffset, lastVisibleOffset, false, true);
|
||||
while (!state.atEnd()) {
|
||||
drawEditorBackgroundForRange(g, state.getStartOffset(), state.getEndOffset(), state.getMergedAttributes(),
|
||||
defaultBackgroundColor, defaultForegroundColor, startX);
|
||||
state.advance();
|
||||
}
|
||||
IterationState state = new IterationState(myEditor, firstVisibleOffset, lastVisibleOffset, null, true, false, true, false);
|
||||
while (!state.atEnd()) {
|
||||
drawEditorBackgroundForRange(g, state.getStartOffset(), state.getEndOffset(), state.getMergedAttributes(),
|
||||
defaultBackgroundColor, defaultForegroundColor, startX);
|
||||
state.advance();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -316,13 +316,11 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedInternalDocu
|
||||
LOG.error("Fold regions must be added or removed inside batchFoldProcessing() only.");
|
||||
return;
|
||||
}
|
||||
if (myEditor.myUseNewRendering) {
|
||||
FoldRegion[] regions = getAllFoldRegions();
|
||||
for (FoldRegion region : regions) {
|
||||
if (!region.isExpanded()) {
|
||||
notifyListenersOnFoldRegionStateChange(region);
|
||||
myFoldRegionsProcessed = true;
|
||||
}
|
||||
FoldRegion[] regions = getAllFoldRegions();
|
||||
for (FoldRegion region : regions) {
|
||||
if (!region.isExpanded()) {
|
||||
notifyListenersOnFoldRegionStateChange(region);
|
||||
myFoldRegionsProcessed = true;
|
||||
}
|
||||
}
|
||||
doClearFoldRegions();
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.editor.ex.RangeHighlighterEx;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUIUtil;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
|
||||
import com.intellij.openapi.editor.impl.view.IterationState;
|
||||
import com.intellij.openapi.editor.markup.EffectType;
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer;
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
|
||||
@@ -284,7 +285,7 @@ class ImmediatePainter {
|
||||
final TextAttributes attributes,
|
||||
final List<RangeHighlighterEx> highlighters) {
|
||||
if (highlighters.size() > 1) {
|
||||
ContainerUtil.quickSort(highlighters, com.intellij.openapi.editor.impl.view.IterationState.BY_LAYER_THEN_ATTRIBUTES);
|
||||
ContainerUtil.quickSort(highlighters, IterationState.BY_LAYER_THEN_ATTRIBUTES);
|
||||
}
|
||||
|
||||
TextAttributes syntax = attributes;
|
||||
|
||||
@@ -1,652 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.ex.*;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.editor.markup.EffectType;
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer;
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public final class IterationState {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.IterationState");
|
||||
|
||||
private static final Comparator<RangeHighlighterEx> BY_LAYER_THEN_ATTRIBUTES = (o1, o2) -> {
|
||||
final int result = LayerComparator.INSTANCE.compare(o1, o2);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// There is a possible case when more than one highlighter target the same region (e.g. 'identifier under caret' and 'identifier').
|
||||
// We want to prefer the one that defines foreground color to the one that doesn't define (has either fore- or background colors
|
||||
// while the other one has only foreground color). See IDEA-85697 for concrete example.
|
||||
final TextAttributes a1 = o1.getTextAttributes();
|
||||
final TextAttributes a2 = o2.getTextAttributes();
|
||||
if (a1 == null ^ a2 == null) {
|
||||
return a1 == null ? 1 : -1;
|
||||
}
|
||||
|
||||
if (a1 == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
final Color fore1 = a1.getForegroundColor();
|
||||
final Color fore2 = a2.getForegroundColor();
|
||||
if (fore1 == null ^ fore2 == null) {
|
||||
return fore1 == null ? 1 : -1;
|
||||
}
|
||||
|
||||
final Color back1 = a1.getBackgroundColor();
|
||||
final Color back2 = a2.getBackgroundColor();
|
||||
if (back1 == null ^ back2 == null) {
|
||||
return back1 == null ? 1 : -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
private final TextAttributes myMergedAttributes = new TextAttributes();
|
||||
|
||||
@Nullable
|
||||
private final HighlighterIterator myHighlighterIterator;
|
||||
private final HighlighterSweep myView;
|
||||
private final HighlighterSweep myDoc;
|
||||
|
||||
private int myStartOffset;
|
||||
|
||||
private int myEndOffset;
|
||||
private final int myEnd;
|
||||
|
||||
private final int[] mySelectionStarts;
|
||||
private final int[] mySelectionEnds;
|
||||
private final int[] myVirtualSelectionStarts;
|
||||
private final int[] myVirtualSelectionEnds;
|
||||
private int myCurrentSelectionIndex = 0;
|
||||
private int myCurrentVirtualSelectionIndex = 0;
|
||||
private boolean myCurrentLineHasVirtualSelection;
|
||||
private int myCurrentPastLineEndBackgroundSegment; // 0 - before selection, 1 - in selection, 2 - after selection
|
||||
private Color myCurrentBackgroundColor;
|
||||
|
||||
private final List<RangeHighlighterEx> myCurrentHighlighters = new ArrayList<>();
|
||||
|
||||
private final FoldingModelEx myFoldingModel;
|
||||
|
||||
private FoldRegion myCurrentFold = null;
|
||||
private final TextAttributes myFoldTextAttributes;
|
||||
private final TextAttributes mySelectionAttributes;
|
||||
private final TextAttributes myCaretRowAttributes;
|
||||
private final Color myDefaultBackground;
|
||||
private final Color myDefaultForeground;
|
||||
private final int myDefaultFontType;
|
||||
private final int myCaretRowStart;
|
||||
private final int myCaretRowEnd;
|
||||
private final List<TextAttributes> myCachedAttributesList = new ArrayList<>(5);
|
||||
private final DocumentEx myDocument;
|
||||
private final EditorEx myEditor;
|
||||
private final Color myReadOnlyColor;
|
||||
private final boolean myUseOnlyFullLineHighlighters;
|
||||
|
||||
private boolean myNextIsFoldRegion;
|
||||
|
||||
public IterationState(@NotNull EditorEx editor, int start, int end, boolean useCaretAndSelection) {
|
||||
this(editor, start, end, useCaretAndSelection, false);
|
||||
}
|
||||
|
||||
public IterationState(@NotNull EditorEx editor, int start, int end, boolean useCaretAndSelection, boolean useOnlyFullLineHighlighters) {
|
||||
this(editor, start, end, useCaretAndSelection, useCaretAndSelection, useOnlyFullLineHighlighters);
|
||||
}
|
||||
|
||||
public IterationState(@NotNull EditorEx editor, int start, int end, boolean useCaretAndSelection, boolean useVirtualSelection, boolean useOnlyFullLineHighlighters) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
myDocument = editor.getDocument();
|
||||
myStartOffset = start;
|
||||
|
||||
myEnd = end;
|
||||
myEditor = editor;
|
||||
myUseOnlyFullLineHighlighters = useOnlyFullLineHighlighters;
|
||||
|
||||
LOG.assertTrue(myStartOffset <= myEnd);
|
||||
myHighlighterIterator = useOnlyFullLineHighlighters ? null : editor.getHighlighter().createIterator(start);
|
||||
|
||||
if (!useCaretAndSelection) {
|
||||
mySelectionStarts = ArrayUtilRt.EMPTY_INT_ARRAY;
|
||||
mySelectionEnds = ArrayUtilRt.EMPTY_INT_ARRAY;
|
||||
myVirtualSelectionStarts = ArrayUtilRt.EMPTY_INT_ARRAY;
|
||||
myVirtualSelectionEnds = ArrayUtilRt.EMPTY_INT_ARRAY;
|
||||
}
|
||||
else {
|
||||
List<Caret> carets = editor.getCaretModel().getAllCarets();
|
||||
mySelectionStarts = new int[carets.size()];
|
||||
mySelectionEnds = new int[carets.size()];
|
||||
myVirtualSelectionStarts = new int[carets.size()];
|
||||
myVirtualSelectionEnds = new int[carets.size()];
|
||||
for (int i = 0; i < carets.size(); i++) {
|
||||
Caret caret = carets.get(i);
|
||||
mySelectionStarts[i] = caret.getSelectionStart();
|
||||
mySelectionEnds[i] = caret.getSelectionEnd();
|
||||
if (useVirtualSelection) {
|
||||
myVirtualSelectionStarts[i] = caret.getSelectionStartPosition().column - editor.offsetToVisualPosition(mySelectionStarts[i]).column;
|
||||
myVirtualSelectionEnds[i] = caret.getSelectionEndPosition().column - editor.offsetToVisualPosition(mySelectionEnds[i]).column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myFoldingModel = editor.getFoldingModel();
|
||||
myFoldTextAttributes = myFoldingModel.getPlaceholderAttributes();
|
||||
mySelectionAttributes = editor.getSelectionModel().getTextAttributes();
|
||||
|
||||
myReadOnlyColor = myEditor.getColorsScheme().getColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR);
|
||||
|
||||
CaretModel caretModel = editor.getCaretModel();
|
||||
myCaretRowAttributes = editor.isRendererMode() ? null : caretModel.getTextAttributes();
|
||||
myDefaultBackground = editor.getColorsScheme().getDefaultBackground();
|
||||
myDefaultForeground = editor.getColorsScheme().getDefaultForeground();
|
||||
TextAttributes defaultAttributes = editor.getColorsScheme().getAttributes(HighlighterColors.TEXT);
|
||||
myDefaultFontType = defaultAttributes == null ? Font.PLAIN : defaultAttributes.getFontType();
|
||||
|
||||
myCaretRowStart = useCaretAndSelection ? caretModel.getVisualLineStart() : -1;
|
||||
myCaretRowEnd = useCaretAndSelection ? caretModel.getVisualLineEnd() : -1;
|
||||
|
||||
MarkupModelEx editorMarkup = editor.getMarkupModel();
|
||||
myView = new HighlighterSweep(editorMarkup, start, myEnd, useOnlyFullLineHighlighters);
|
||||
|
||||
MarkupModelEx docMarkup = editor.getFilteredDocumentMarkupModel();
|
||||
myDoc = new HighlighterSweep(docMarkup, start, myEnd, useOnlyFullLineHighlighters);
|
||||
|
||||
myEndOffset = myStartOffset;
|
||||
|
||||
advance();
|
||||
}
|
||||
|
||||
private class HighlighterSweep {
|
||||
private RangeHighlighterEx myNextHighlighter;
|
||||
int i;
|
||||
private final RangeHighlighterEx[] highlighters;
|
||||
|
||||
private HighlighterSweep(@NotNull MarkupModelEx markupModel, int start, int end, final boolean onlyFullLine) {
|
||||
// we have to get all highlighters in advance and sort them by affected offsets
|
||||
// since these can be different from the real offsets the highlighters are sorted by in the tree. (See LINES_IN_RANGE perverts)
|
||||
final List<RangeHighlighterEx> list = new ArrayList<>();
|
||||
markupModel.processRangeHighlightersOverlappingWith(start, end, new CommonProcessors.CollectProcessor<RangeHighlighterEx>(list) {
|
||||
@Override
|
||||
protected boolean accept(RangeHighlighterEx ex) {
|
||||
return !onlyFullLine || ex.getTargetArea() == HighlighterTargetArea.LINES_IN_RANGE;
|
||||
}
|
||||
});
|
||||
highlighters = list.isEmpty() ? RangeHighlighterEx.EMPTY_ARRAY : list.toArray(new RangeHighlighterEx[list.size()]);
|
||||
Arrays.sort(highlighters, RangeHighlighterEx.BY_AFFECTED_START_OFFSET);
|
||||
|
||||
while (i < highlighters.length) {
|
||||
RangeHighlighterEx highlighter = highlighters[i++];
|
||||
if (!skipHighlighter(highlighter)) {
|
||||
myNextHighlighter = highlighter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advance() {
|
||||
if (myNextHighlighter != null) {
|
||||
if (myNextHighlighter.getAffectedAreaStartOffset() > myStartOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
myCurrentHighlighters.add(myNextHighlighter);
|
||||
myNextHighlighter = null;
|
||||
}
|
||||
|
||||
while (i < highlighters.length) {
|
||||
RangeHighlighterEx highlighter = highlighters[i++];
|
||||
if (!skipHighlighter(highlighter)) {
|
||||
if (highlighter.getAffectedAreaStartOffset() > myStartOffset) {
|
||||
myNextHighlighter = highlighter;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
myCurrentHighlighters.add(highlighter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getMinSegmentHighlighterEnd() {
|
||||
if (myNextHighlighter != null) {
|
||||
return myNextHighlighter.getAffectedAreaStartOffset();
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean skipHighlighter(@NotNull RangeHighlighterEx highlighter) {
|
||||
if (!highlighter.isValid() || highlighter.isAfterEndOfLine() || highlighter.getTextAttributes() == null) return true;
|
||||
final FoldRegion region = myFoldingModel.getCollapsedRegionAtOffset(highlighter.getAffectedAreaStartOffset());
|
||||
if (region != null && region == myFoldingModel.getCollapsedRegionAtOffset(highlighter.getAffectedAreaEndOffset())) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void advance() {
|
||||
myNextIsFoldRegion = false;
|
||||
myStartOffset = myEndOffset;
|
||||
advanceSegmentHighlighters();
|
||||
advanceCurrentSelectionIndex();
|
||||
advanceCurrentVirtualSelectionIndex();
|
||||
|
||||
if (!myUseOnlyFullLineHighlighters) {
|
||||
myCurrentFold = myFoldingModel.getCollapsedRegionAtOffset(myStartOffset);
|
||||
}
|
||||
if (myCurrentFold != null) {
|
||||
myEndOffset = myCurrentFold.getEndOffset();
|
||||
}
|
||||
else {
|
||||
myEndOffset = Math.min(getHighlighterEnd(myStartOffset), getSelectionEnd());
|
||||
myEndOffset = Math.min(myEndOffset, getMinSegmentHighlightersEnd());
|
||||
int foldRangesEnd = getFoldRangesEnd(myStartOffset);
|
||||
myEndOffset = Math.min(myEndOffset, foldRangesEnd);
|
||||
myEndOffset = Math.min(myEndOffset, getCaretEnd(myStartOffset));
|
||||
myEndOffset = Math.min(myEndOffset, getGuardedBlockEnd(myStartOffset));
|
||||
|
||||
myNextIsFoldRegion = myEndOffset == foldRangesEnd && myEndOffset < myEnd;
|
||||
}
|
||||
|
||||
reinit();
|
||||
}
|
||||
|
||||
private int getHighlighterEnd(int start) {
|
||||
if (myHighlighterIterator == null) {
|
||||
return myEnd;
|
||||
}
|
||||
while (!myHighlighterIterator.atEnd()) {
|
||||
int end = myHighlighterIterator.getEnd();
|
||||
if (end > start) {
|
||||
return end;
|
||||
}
|
||||
myHighlighterIterator.advance();
|
||||
}
|
||||
return myEnd;
|
||||
}
|
||||
|
||||
private int getCaretEnd(int start) {
|
||||
if (myCaretRowStart > start) {
|
||||
return myCaretRowStart;
|
||||
}
|
||||
|
||||
if (myCaretRowEnd > start) {
|
||||
return myCaretRowEnd;
|
||||
}
|
||||
|
||||
return myEnd;
|
||||
}
|
||||
|
||||
private int getGuardedBlockEnd(int start) {
|
||||
if (myUseOnlyFullLineHighlighters) {
|
||||
return myEnd;
|
||||
}
|
||||
List<RangeMarker> blocks = myDocument.getGuardedBlocks();
|
||||
int min = myEnd;
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
RangeMarker block = blocks.get(i);
|
||||
if (block.getStartOffset() > start) {
|
||||
min = Math.min(min, block.getStartOffset());
|
||||
}
|
||||
else if (block.getEndOffset() > start) {
|
||||
min = Math.min(min, block.getEndOffset());
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
private void advanceCurrentSelectionIndex() {
|
||||
while (myCurrentSelectionIndex < mySelectionEnds.length && myStartOffset >= mySelectionEnds[myCurrentSelectionIndex]) {
|
||||
myCurrentSelectionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceCurrentVirtualSelectionIndex() {
|
||||
while (myCurrentVirtualSelectionIndex < mySelectionEnds.length
|
||||
&& (myStartOffset > mySelectionEnds[myCurrentVirtualSelectionIndex] || myVirtualSelectionEnds[myCurrentVirtualSelectionIndex] <= 0)) {
|
||||
myCurrentVirtualSelectionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private int getSelectionEnd() {
|
||||
if (myCurrentSelectionIndex >= mySelectionStarts.length) {
|
||||
return myEnd;
|
||||
}
|
||||
if (myStartOffset < mySelectionStarts[myCurrentSelectionIndex]) {
|
||||
return mySelectionStarts[myCurrentSelectionIndex];
|
||||
}
|
||||
return mySelectionEnds[myCurrentSelectionIndex];
|
||||
}
|
||||
|
||||
private boolean isInSelection() {
|
||||
return myCurrentSelectionIndex < mySelectionStarts.length && myStartOffset >= mySelectionStarts[myCurrentSelectionIndex];
|
||||
}
|
||||
|
||||
private void advanceSegmentHighlighters() {
|
||||
myDoc.advance();
|
||||
myView.advance();
|
||||
|
||||
for (int i = myCurrentHighlighters.size() - 1; i >= 0; i--) {
|
||||
RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
|
||||
if (highlighter.getAffectedAreaEndOffset() <= myStartOffset) {
|
||||
myCurrentHighlighters.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getFoldRangesEnd(int startOffset) {
|
||||
if (myUseOnlyFullLineHighlighters) {
|
||||
return myEnd;
|
||||
}
|
||||
int end = myEnd;
|
||||
FoldRegion[] topLevelCollapsed = myFoldingModel.fetchTopLevel();
|
||||
if (topLevelCollapsed != null) {
|
||||
for (int i = myFoldingModel.getLastCollapsedRegionBefore(startOffset) + 1;
|
||||
i >= 0 && i < topLevelCollapsed.length;
|
||||
i++) {
|
||||
FoldRegion range = topLevelCollapsed[i];
|
||||
if (!range.isValid()) continue;
|
||||
|
||||
int rangeEnd = range.getStartOffset();
|
||||
if (rangeEnd > startOffset) {
|
||||
if (rangeEnd < end) {
|
||||
end = rangeEnd;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
private int getMinSegmentHighlightersEnd() {
|
||||
int end = myEnd;
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < myCurrentHighlighters.size(); i++) {
|
||||
RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
|
||||
if (highlighter.getAffectedAreaEndOffset() < end) {
|
||||
end = highlighter.getAffectedAreaEndOffset();
|
||||
}
|
||||
}
|
||||
|
||||
end = Math.min(end, myDoc.getMinSegmentHighlighterEnd());
|
||||
end = Math.min(end, myView.getMinSegmentHighlighterEnd());
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
private void reinit() {
|
||||
if (myHighlighterIterator != null && myHighlighterIterator.atEnd()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isInSelection = isInSelection();
|
||||
boolean isInCaretRow = myStartOffset >= myCaretRowStart && myStartOffset < myCaretRowEnd;
|
||||
boolean isInGuardedBlock = !myUseOnlyFullLineHighlighters && myDocument.getOffsetGuard(myStartOffset) != null;
|
||||
|
||||
TextAttributes syntax = myHighlighterIterator == null ? null : myHighlighterIterator.getTextAttributes();
|
||||
|
||||
TextAttributes selection = isInSelection ? mySelectionAttributes : null;
|
||||
TextAttributes caret = isInCaretRow ? myCaretRowAttributes : null;
|
||||
TextAttributes fold = myCurrentFold != null ? myFoldTextAttributes : null;
|
||||
TextAttributes guard = isInGuardedBlock
|
||||
? new TextAttributes(null, myReadOnlyColor, null, EffectType.BOXED, Font.PLAIN)
|
||||
: null;
|
||||
|
||||
final int size = myCurrentHighlighters.size();
|
||||
if (size > 1) {
|
||||
ContainerUtil.quickSort(myCurrentHighlighters, BY_LAYER_THEN_ATTRIBUTES);
|
||||
}
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < size; i++) {
|
||||
RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
|
||||
if (highlighter.getTextAttributes() == TextAttributes.ERASE_MARKER) {
|
||||
syntax = null;
|
||||
}
|
||||
}
|
||||
|
||||
List<TextAttributes> cachedAttributes = myCachedAttributesList;
|
||||
cachedAttributes.clear();
|
||||
|
||||
int selectionAttributesIndex = -1; // a 'would-be' or real position of selection attributes in attributes list
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < size; i++) {
|
||||
RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
|
||||
if (highlighter.getLayer() < HighlighterLayer.SELECTION) {
|
||||
if (selectionAttributesIndex < 0) {
|
||||
selectionAttributesIndex = cachedAttributes.size();
|
||||
}
|
||||
if (selection != null) {
|
||||
cachedAttributes.add(selection);
|
||||
selection = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (fold != null && highlighter.getLayer() < HighlighterLayer.GUARDED_BLOCKS) {
|
||||
cachedAttributes.add(fold);
|
||||
fold = null;
|
||||
}
|
||||
|
||||
if (guard != null && highlighter.getLayer() < HighlighterLayer.GUARDED_BLOCKS) {
|
||||
cachedAttributes.add(guard);
|
||||
guard = null;
|
||||
}
|
||||
|
||||
if (caret != null && highlighter.getLayer() < HighlighterLayer.CARET_ROW) {
|
||||
cachedAttributes.add(caret);
|
||||
caret = null;
|
||||
}
|
||||
|
||||
if (syntax != null && highlighter.getLayer() < HighlighterLayer.SYNTAX) {
|
||||
cachedAttributes.add(syntax);
|
||||
syntax = null;
|
||||
}
|
||||
|
||||
TextAttributes textAttributes = highlighter.getTextAttributes();
|
||||
if (textAttributes != null && textAttributes != TextAttributes.ERASE_MARKER) {
|
||||
cachedAttributes.add(textAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionAttributesIndex < 0) {
|
||||
selectionAttributesIndex = cachedAttributes.size();
|
||||
}
|
||||
if (selection != null) cachedAttributes.add(selection);
|
||||
if (fold != null) cachedAttributes.add(fold);
|
||||
if (guard != null) cachedAttributes.add(guard);
|
||||
if (caret != null) cachedAttributes.add(caret);
|
||||
if (syntax != null) cachedAttributes.add(syntax);
|
||||
|
||||
Color fore = null;
|
||||
Color back = isInGuardedBlock ? myReadOnlyColor : null;
|
||||
Color effect = null;
|
||||
EffectType effectType = null;
|
||||
int fontType = 0;
|
||||
|
||||
boolean selectionBackgroundIsPotentiallyVisible = cachedAttributes.isEmpty();
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < cachedAttributes.size(); i++) {
|
||||
TextAttributes attrs = cachedAttributes.get(i);
|
||||
|
||||
if (fore == null) {
|
||||
fore = attrs.getForegroundColor();
|
||||
}
|
||||
|
||||
if (back == null) {
|
||||
if (isInSelection && i == selectionAttributesIndex || !isInSelection && i >= selectionAttributesIndex) {
|
||||
selectionBackgroundIsPotentiallyVisible = true;
|
||||
}
|
||||
back = attrs.getBackgroundColor();
|
||||
}
|
||||
|
||||
if (fontType == Font.PLAIN) {
|
||||
fontType = attrs.getFontType();
|
||||
}
|
||||
|
||||
if (effect == null) {
|
||||
effect = attrs.getEffectColor();
|
||||
effectType = attrs.getEffectType();
|
||||
}
|
||||
}
|
||||
|
||||
if (fore == null) fore = myDefaultForeground;
|
||||
if (back == null) back = myDefaultBackground;
|
||||
if (effectType == null) effectType = EffectType.BOXED;
|
||||
if (fontType == Font.PLAIN) fontType = myDefaultFontType;
|
||||
|
||||
myMergedAttributes.setAttributes(fore, back, effect, null, effectType, fontType);
|
||||
|
||||
myCurrentBackgroundColor = back;
|
||||
if (selectionBackgroundIsPotentiallyVisible && myCurrentVirtualSelectionIndex < mySelectionStarts.length && myStartOffset == mySelectionEnds[myCurrentVirtualSelectionIndex]) {
|
||||
myCurrentLineHasVirtualSelection = true;
|
||||
myCurrentPastLineEndBackgroundSegment = myVirtualSelectionStarts[myCurrentVirtualSelectionIndex] > 0 ? 0 : 1;
|
||||
}
|
||||
else {
|
||||
myCurrentLineHasVirtualSelection = false;
|
||||
myCurrentPastLineEndBackgroundSegment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean atEnd() {
|
||||
return myStartOffset >= myEnd;
|
||||
}
|
||||
|
||||
|
||||
public int getStartOffset() {
|
||||
return myStartOffset;
|
||||
}
|
||||
|
||||
public int getEndOffset() {
|
||||
return myEndOffset;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextAttributes getMergedAttributes() {
|
||||
return myMergedAttributes;
|
||||
}
|
||||
|
||||
public FoldRegion getCurrentFold() {
|
||||
return myCurrentFold;
|
||||
}
|
||||
|
||||
public boolean nextIsFoldRegion() {
|
||||
return myNextIsFoldRegion;
|
||||
}
|
||||
|
||||
public boolean hasPastLineEndBackgroundSegment() {
|
||||
return myCurrentLineHasVirtualSelection && myCurrentPastLineEndBackgroundSegment < 2;
|
||||
}
|
||||
|
||||
public int getPastLineEndBackgroundSegmentWidth() {
|
||||
switch (myCurrentPastLineEndBackgroundSegment) {
|
||||
case 0: return myVirtualSelectionStarts[myCurrentVirtualSelectionIndex];
|
||||
case 1: return myVirtualSelectionEnds[myCurrentVirtualSelectionIndex] - myVirtualSelectionStarts[myCurrentVirtualSelectionIndex];
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextAttributes getPastLineEndBackgroundAttributes() {
|
||||
myMergedAttributes.setBackgroundColor(myCurrentPastLineEndBackgroundSegment == 1 ? mySelectionAttributes.getBackgroundColor() : myCurrentBackgroundColor);
|
||||
return myMergedAttributes;
|
||||
}
|
||||
|
||||
public void advanceToNextPastLineEndBackgroundSegment() {
|
||||
myCurrentPastLineEndBackgroundSegment++;
|
||||
}
|
||||
|
||||
public boolean hasPastFileEndBackgroundSegments() {
|
||||
myCurrentLineHasVirtualSelection = myVirtualSelectionEnds.length > 0
|
||||
&& myVirtualSelectionEnds[myVirtualSelectionEnds.length - 1] > 0
|
||||
&& myEndOffset == myEnd
|
||||
&& mySelectionEnds[mySelectionStarts.length - 1] == myEndOffset;
|
||||
if (myCurrentLineHasVirtualSelection) {
|
||||
myCurrentVirtualSelectionIndex = myVirtualSelectionStarts.length - 1;
|
||||
myCurrentPastLineEndBackgroundSegment = myVirtualSelectionStarts[myCurrentVirtualSelectionIndex] > 0 ? 0 : 1;
|
||||
myCurrentBackgroundColor = myEndOffset >= myCaretRowStart ? myCaretRowAttributes.getBackgroundColor() : myDefaultBackground;
|
||||
}
|
||||
return myCurrentLineHasVirtualSelection;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Color getPastFileEndBackground() {
|
||||
boolean isInCaretRow = myEditor.getCaretModel().getLogicalPosition().line >= myDocument.getLineCount() - 1;
|
||||
|
||||
Color caret = isInCaretRow && myCaretRowAttributes != null ? myCaretRowAttributes.getBackgroundColor() : null;
|
||||
|
||||
ContainerUtil.quickSort(myCurrentHighlighters, LayerComparator.INSTANCE);
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < myCurrentHighlighters.size(); i++) {
|
||||
RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
|
||||
if (caret != null && highlighter.getLayer() < HighlighterLayer.CARET_ROW) {
|
||||
return caret;
|
||||
}
|
||||
|
||||
if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE
|
||||
|| myDocument.getLineNumber(highlighter.getEndOffset()) < myDocument.getLineCount() - 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TextAttributes textAttributes = highlighter.getTextAttributes();
|
||||
if (textAttributes != null) {
|
||||
Color backgroundColor = textAttributes.getBackgroundColor();
|
||||
if (backgroundColor != null) return backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
return caret;
|
||||
}
|
||||
|
||||
private static class LayerComparator implements Comparator<RangeHighlighterEx> {
|
||||
private static final LayerComparator INSTANCE = new LayerComparator();
|
||||
@Override
|
||||
public int compare(RangeHighlighterEx o1, RangeHighlighterEx o2) {
|
||||
int layerDiff = o2.getLayer() - o1.getLayer();
|
||||
if (layerDiff != 0) {
|
||||
return layerDiff;
|
||||
}
|
||||
// prefer more specific region
|
||||
int o1Length = o1.getAffectedAreaEndOffset() - o1.getAffectedAreaStartOffset();
|
||||
int o2Length = o2.getAffectedAreaEndOffset() - o2.getAffectedAreaStartOffset();
|
||||
return o1Length - o2Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,13 +141,7 @@ public class ScrollingModelImpl implements ScrollingModelEx {
|
||||
public void scrollToCaret(@NotNull ScrollType scrollType) {
|
||||
assertIsDispatchThread();
|
||||
myEditor.validateSize();
|
||||
if (myEditor.myUseNewRendering) {
|
||||
AsyncEditorLoader.performWhenLoaded(myEditor, () -> scrollTo(myEditor.getCaretModel().getVisualPosition(), scrollType));
|
||||
}
|
||||
else {
|
||||
LogicalPosition caretPosition = myEditor.getCaretModel().getLogicalPosition();
|
||||
scrollTo(caretPosition, scrollType);
|
||||
}
|
||||
AsyncEditorLoader.performWhenLoaded(myEditor, () -> scrollTo(myEditor.getCaretModel().getVisualPosition(), scrollType));
|
||||
}
|
||||
|
||||
private void scrollTo(@NotNull VisualPosition pos, @NotNull ScrollType scrollType) {
|
||||
|
||||
+17
-321
@@ -29,7 +29,6 @@ import com.intellij.openapi.editor.impl.softwrap.*;
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.CachingSoftWrapDataMapper;
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapApplianceManager;
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapAwareDocumentParsingListenerAdapter;
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapAwareVisualSizeManager;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -59,19 +58,8 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
PropertyChangeListener, Dumpable, Disposable
|
||||
{
|
||||
|
||||
/**
|
||||
* Holds name of JVM property which presence should trigger debug-aware soft wraps processing.
|
||||
*/
|
||||
private static final String DEBUG_PROPERTY_NAME = "idea.editor.wrap.soft.debug";
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#" + SoftWrapModelImpl.class.getName());
|
||||
|
||||
private final LogicalPositionToOffsetTask myLogicalToOffsetTask = new LogicalPositionToOffsetTask();
|
||||
private final OffsetToLogicalTask myOffsetToLogicalTask = new OffsetToLogicalTask();
|
||||
private final VisualToLogicalTask myVisualToLogicalTask = new VisualToLogicalTask();
|
||||
private final LogicalToVisualTask myLogicalToVisualTask = new LogicalToVisualTask();
|
||||
private final FoldProcessingEndTask myFoldProcessingEndTask = new FoldProcessingEndTask();
|
||||
|
||||
private final List<SoftWrapChangeListener> mySoftWrapListeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
@@ -88,14 +76,11 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
private final SoftWrapsStorage myStorage;
|
||||
private SoftWrapPainter myPainter;
|
||||
private final SoftWrapApplianceManager myApplianceManager;
|
||||
private final SoftWrapAwareVisualSizeManager myVisualSizeManager;
|
||||
private EditorTextRepresentationHelper myEditorTextRepresentationHelper;
|
||||
|
||||
@NotNull
|
||||
private final EditorImpl myEditor;
|
||||
|
||||
/** Holds number of 'active' calls, i.e. number of methods calls of the current object within the current call stack. */
|
||||
private int myActive;
|
||||
private boolean myUseSoftWraps;
|
||||
private int myTabWidth = -1;
|
||||
private final FontPreferences myFontPreferences = new FontPreferences();
|
||||
@@ -133,9 +118,7 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
myEditorTextRepresentationHelper = new DefaultEditorTextRepresentationHelper(editor);
|
||||
myDataMapper = new CachingSoftWrapDataMapper(editor, myStorage);
|
||||
myApplianceManager = new SoftWrapApplianceManager(myStorage, editor, myPainter, myDataMapper);
|
||||
myVisualSizeManager = new SoftWrapAwareVisualSizeManager(myPainter);
|
||||
|
||||
myApplianceManager.addListener(myVisualSizeManager);
|
||||
myApplianceManager.addListener(new SoftWrapAwareDocumentParsingListenerAdapter() {
|
||||
@Override
|
||||
public void recalculationEnds() {
|
||||
@@ -154,8 +137,8 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
}
|
||||
|
||||
private boolean areSoftWrapsEnabledInEditor() {
|
||||
return myEditor.getSettings().isUseSoftWraps() && (!myEditor.myUseNewRendering || !myEditor.isOneLineMode())
|
||||
&& (!(myEditor.getDocument() instanceof DocumentImpl) || !((DocumentImpl)myEditor.getDocument()).acceptsSlashR());
|
||||
return myEditor.getSettings().isUseSoftWraps() && !myEditor.isOneLineMode() &&
|
||||
(!(myEditor.getDocument() instanceof DocumentImpl) || !((DocumentImpl)myEditor.getDocument()).acceptsSlashR());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,9 +164,7 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
myApplianceManager.reset();
|
||||
myDeferredFoldRegions.clear();
|
||||
myStorage.removeAll();
|
||||
if (myEditor.myUseNewRendering) {
|
||||
myEditor.myView.reinitSettings();
|
||||
}
|
||||
myEditor.myView.reinitSettings();
|
||||
myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
|
||||
}
|
||||
}
|
||||
@@ -201,10 +182,7 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
@Override
|
||||
public boolean isSoftWrappingEnabled() {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
return myUseSoftWraps &&
|
||||
(myEditor.myUseNewRendering || !myEditor.isOneLineMode()) &&
|
||||
!myEditor.isPurePaintingMode() &&
|
||||
!myApplianceManager.getAvailableArea().isEmpty();
|
||||
return myUseSoftWraps && !myEditor.isPurePaintingMode() && !myApplianceManager.getAvailableArea().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,7 +196,7 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
|
||||
@Override
|
||||
public int getSoftWrapIndex(int offset) {
|
||||
if (myEditor.myUseNewRendering && !isSoftWrappingEnabled()) {
|
||||
if (!isSoftWrappingEnabled()) {
|
||||
return -1;
|
||||
}
|
||||
return myStorage.getSoftWrapIndex(offset);
|
||||
@@ -275,25 +253,6 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
return myStorage.getSoftWraps().size(); // Assuming that soft wrap has single line feed all the time
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method that is expected to be invoked before editor painting.
|
||||
* <p/>
|
||||
* It's primary purpose is to recalculate soft wraps at least for the painted area if necessary.
|
||||
*/
|
||||
public void registerSoftWrapsIfNecessary() {
|
||||
if (!isSoftWrappingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
myApplianceManager.registerSoftWrapIfNecessary();
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends SoftWrap> getRegisteredSoftWraps() {
|
||||
if (!isSoftWrappingEnabled()) {
|
||||
@@ -344,88 +303,6 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
return myPainter.getMinDrawingWidth(drawingType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual) {
|
||||
if (!prepareToMapping()) {
|
||||
return myEditor.visualToLogicalPosition(visual, false);
|
||||
}
|
||||
myActive++;
|
||||
try {
|
||||
myVisualToLogicalTask.input = visual;
|
||||
executeSafely(myVisualToLogicalTask);
|
||||
return myVisualToLogicalTask.output;
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
if (!prepareToMapping()) {
|
||||
return myEditor.offsetToLogicalPosition(offset, false);
|
||||
}
|
||||
myActive++;
|
||||
try {
|
||||
myOffsetToLogicalTask.input = offset;
|
||||
executeSafely(myOffsetToLogicalTask);
|
||||
return myOffsetToLogicalTask.output;
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int logicalPositionToOffset(@NotNull LogicalPosition logicalPosition) {
|
||||
if (!prepareToMapping()) {
|
||||
return myEditor.logicalPositionToOffset(logicalPosition, false);
|
||||
}
|
||||
myActive++;
|
||||
try {
|
||||
myLogicalToOffsetTask.input = logicalPosition;
|
||||
executeSafely(myLogicalToOffsetTask);
|
||||
return myLogicalToOffsetTask.output;
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition adjustLogicalPosition(LogicalPosition defaultLogical, int offset) {
|
||||
if (!prepareToMapping()) {
|
||||
return defaultLogical;
|
||||
}
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
myOffsetToLogicalTask.input = offset;
|
||||
executeSafely(myOffsetToLogicalTask);
|
||||
return myOffsetToLogicalTask.output;
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual) {
|
||||
if (!prepareToMapping()) {
|
||||
return defaultVisual;
|
||||
}
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
myLogicalToVisualTask.input = logical;
|
||||
myLogicalToVisualTask.defaultOutput = defaultVisual;
|
||||
executeSafely(myLogicalToVisualTask);
|
||||
return myLogicalToVisualTask.output;
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates preparations for performing document dimension mapping (e.g. visual to logical position) and answers
|
||||
* if soft wraps-aware processing should be used (e.g. there is no need to consider soft wraps if user configured them
|
||||
@@ -434,7 +311,7 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
* @return <code>true</code> if soft wraps-aware processing should be used; <code>false</code> otherwise
|
||||
*/
|
||||
public boolean prepareToMapping() {
|
||||
if (myUpdateInProgress || myBulkUpdateInProgress || myActive > 0 || !isSoftWrappingEnabled()) {
|
||||
if (myUpdateInProgress || myBulkUpdateInProgress || !isSoftWrappingEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -499,29 +376,9 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
return false;
|
||||
}
|
||||
|
||||
if (myEditor.myUseNewRendering) {
|
||||
VisualPosition beforeSoftWrap = myEditor.offsetToVisualPosition(offset, true, true);
|
||||
return visual.line > beforeSoftWrap.line ||
|
||||
visual.column > beforeSoftWrap.column || visual.column == beforeSoftWrap.column && countBeforeSoftWrap;
|
||||
}
|
||||
else {
|
||||
VisualPosition visualBeforeSoftWrap = myEditor.offsetToVisualPosition(offset - 1);
|
||||
int x = 0;
|
||||
LogicalPosition logLineStart = myEditor.visualToLogicalPosition(new VisualPosition(visualBeforeSoftWrap.line, 0));
|
||||
if (logLineStart.softWrapLinesOnCurrentLogicalLine > 0) {
|
||||
int offsetLineStart = myEditor.logicalPositionToOffset(logLineStart);
|
||||
softWrap = model.getSoftWrap(offsetLineStart);
|
||||
if (softWrap != null) {
|
||||
x = softWrap.getIndentInPixels();
|
||||
}
|
||||
}
|
||||
int width = EditorUtil.textWidthInColumns(myEditor, myEditor.getDocument().getCharsSequence(), offset - 1, offset, x);
|
||||
int softWrapStartColumn = visualBeforeSoftWrap.column + width;
|
||||
if (visual.line > visualBeforeSoftWrap.line) {
|
||||
return true;
|
||||
}
|
||||
return countBeforeSoftWrap ? visual.column >= softWrapStartColumn : visual.column > softWrapStartColumn;
|
||||
}
|
||||
VisualPosition beforeSoftWrap = myEditor.offsetToVisualPosition(offset, true, true);
|
||||
return visual.line > beforeSoftWrap.line ||
|
||||
visual.column > beforeSoftWrap.column || visual.column == beforeSoftWrap.column && countBeforeSoftWrap;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -547,10 +404,6 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
return myStorage.addSoftWrapChangeListener(listener);
|
||||
}
|
||||
|
||||
public boolean addVisualSizeChangeListener(@NotNull VisualSizeChangeListener listener) {
|
||||
return myVisualSizeManager.addVisualSizeChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return EditorDocumentPriorities.SOFT_WRAP_MODEL;
|
||||
@@ -625,11 +478,13 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
if (!isSoftWrappingEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (myEditor.myUseNewRendering) {
|
||||
myFoldProcessingEndTask.run(true);
|
||||
try {
|
||||
if (!myDirty) { // no need to recalculate specific areas if the whole document will be reprocessed
|
||||
myApplianceManager.recalculate(myDeferredFoldRegions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
executeSafely(myFoldProcessingEndTask);
|
||||
finally {
|
||||
myDeferredFoldRegions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,11 +507,6 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
myDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CachingSoftWrapDataMapper getDataMapper() {
|
||||
return myDataMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
@@ -665,7 +515,6 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
myDataMapper.release();
|
||||
myApplianceManager.release();
|
||||
myStorage.removeAll();
|
||||
myDeferredFoldRegions.clear();
|
||||
@@ -682,47 +531,10 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
return myApplianceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* We know that there are problems with incremental soft wraps cache update at the moment. Hence, we may implement full cache
|
||||
* reconstruction when the problem is encountered in order to avoid customer annoyance.
|
||||
* <p/>
|
||||
* However, the problems still should be fixed, hence, we report them only if dedicated flag is set.
|
||||
* <p/>
|
||||
* Current method encapsulates the logic mentioned above.
|
||||
*
|
||||
* @param task command object that which execution may trigger incremental update of update soft wraps cache
|
||||
*/
|
||||
@SuppressWarnings({"UseOfArchaicSystemPropertyAccessors"})
|
||||
private void executeSafely(SoftWrapAwareTask task) {
|
||||
try {
|
||||
task.run(true);
|
||||
} catch (Throwable e) {
|
||||
if (Boolean.getBoolean(DEBUG_PROPERTY_NAME) || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
String info = myEditor.dumpState();
|
||||
LOG.error(String.format("Unexpected exception occurred during performing '%s'", task), e, info);
|
||||
}
|
||||
myEditor.getFoldingModel().rebuild();
|
||||
myDataMapper.release();
|
||||
myApplianceManager.reset();
|
||||
myStorage.removeAll();
|
||||
myApplianceManager.recalculateIfNecessary();
|
||||
try {
|
||||
task.run(true);
|
||||
}
|
||||
catch (Throwable e1) {
|
||||
String info = myEditor.dumpState();
|
||||
LOG.error(String.format("Can't perform %s even with complete soft wraps cache re-parsing", task), e1, info);
|
||||
myEditor.getSettings().setUseSoftWraps(false);
|
||||
task.run(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void setSoftWrapPainter(SoftWrapPainter painter) {
|
||||
myPainter = painter;
|
||||
myApplianceManager.setSoftWrapPainter(painter);
|
||||
myVisualSizeManager.setSoftWrapPainter(painter);
|
||||
}
|
||||
|
||||
public static EditorTextRepresentationHelper getEditorTextRepresentationHelper(@NotNull Editor editor) {
|
||||
@@ -743,9 +555,9 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
@Override
|
||||
public String dumpState() {
|
||||
return String.format("\nuse soft wraps: %b, tab width: %d, additional columns: %b, " +
|
||||
"update in progress: %b, bulk update in progress: %b, active: %d, dirty: %b, deferred regions: %s" +
|
||||
"update in progress: %b, bulk update in progress: %b, dirty: %b, deferred regions: %s" +
|
||||
"\nappliance manager state: %s\nsoft wraps mapping info: %s\nsoft wraps: %s",
|
||||
myUseSoftWraps, myTabWidth, myForceAdditionalColumns, myUpdateInProgress, myBulkUpdateInProgress, myActive,
|
||||
myUseSoftWraps, myTabWidth, myForceAdditionalColumns, myUpdateInProgress, myBulkUpdateInProgress,
|
||||
myDirty, myDeferredFoldRegions.toString(),
|
||||
myApplianceManager.dumpState(), myDataMapper.dumpState(), myStorage.dumpState());
|
||||
}
|
||||
@@ -758,120 +570,4 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter
|
||||
public boolean isDirty() {
|
||||
return myUseSoftWraps && myDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines generic interface for the command that may be proceeded in both <code>'soft wraps aware'</code> and
|
||||
* <code>'soft wraps unaware'</code> modes.
|
||||
*/
|
||||
private interface SoftWrapAwareTask {
|
||||
|
||||
/**
|
||||
* Asks current task to do the job.
|
||||
* <p/>
|
||||
* It's assumed that input data (if any) is already stored at the task object. Processing result (if any) is assumed
|
||||
* to be stored there as well for further retrieval in implementation-specific manner.
|
||||
*
|
||||
* @param softWrapAware flag that indicates if soft wraps-aware processing should be performed
|
||||
* @throws IllegalStateException in case of inability to do the job
|
||||
*/
|
||||
void run(boolean softWrapAware) throws IllegalStateException;
|
||||
}
|
||||
|
||||
private class OffsetToLogicalTask implements SoftWrapAwareTask {
|
||||
|
||||
public int input;
|
||||
public LogicalPosition output;
|
||||
|
||||
@Override
|
||||
public void run(boolean softWrapAware) throws IllegalStateException {
|
||||
if (softWrapAware) {
|
||||
output = myDataMapper.offsetToLogicalPosition(input);
|
||||
}
|
||||
else {
|
||||
output = myEditor.offsetToLogicalPosition(input, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mapping from offset (" + input + ") to logical position";
|
||||
}
|
||||
}
|
||||
|
||||
private class VisualToLogicalTask implements SoftWrapAwareTask {
|
||||
|
||||
public VisualPosition input;
|
||||
public LogicalPosition output;
|
||||
|
||||
@Override
|
||||
public void run(boolean softWrapAware) throws IllegalStateException {
|
||||
if (softWrapAware) {
|
||||
output = myDataMapper.visualToLogical(input);
|
||||
}
|
||||
else {
|
||||
output = myEditor.visualToLogicalPosition(input, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mapping from visual position (" + input + ") to logical position";
|
||||
}
|
||||
}
|
||||
|
||||
private class LogicalToVisualTask implements SoftWrapAwareTask {
|
||||
|
||||
public LogicalPosition input;
|
||||
private VisualPosition defaultOutput;
|
||||
public VisualPosition output;
|
||||
|
||||
@Override
|
||||
public void run(boolean softWrapAware) throws IllegalStateException {
|
||||
output = softWrapAware ? myDataMapper.logicalToVisualPosition(input, defaultOutput) : defaultOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mapping from logical position (" + input + ") to visual position";
|
||||
}
|
||||
}
|
||||
|
||||
private class LogicalPositionToOffsetTask implements SoftWrapAwareTask {
|
||||
|
||||
public LogicalPosition input;
|
||||
public int output;
|
||||
|
||||
@Override
|
||||
public void run(boolean softWrapAware) throws IllegalStateException {
|
||||
output = softWrapAware ? myDataMapper.logicalPositionToOffset(input) : myEditor.logicalPositionToOffset(input, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mapping from logical position (" + input + ") to offset";
|
||||
}
|
||||
}
|
||||
|
||||
private class FoldProcessingEndTask implements SoftWrapAwareTask {
|
||||
@Override
|
||||
public void run(boolean softWrapAware) {
|
||||
if (!softWrapAware) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!myDirty) { // no need to recalculate specific areas if the whole document will be reprocessed
|
||||
myApplianceManager.recalculate(myDeferredFoldRegions);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
myDeferredFoldRegions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "fold regions state change processing";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap;
|
||||
|
||||
/**
|
||||
* Strategy interface for ruling if soft wraps should be processed.
|
||||
* <p/>
|
||||
* Implementations of this interface are not obliged to be thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 10/6/10 8:08 AM
|
||||
*/
|
||||
public interface SoftWrapApplianceStrategy {
|
||||
|
||||
/**
|
||||
* Allows to answer if soft wraps should be processed.
|
||||
*
|
||||
* @return <code>true</code> if soft wraps should be processed; <code>false</code> otherwise
|
||||
*/
|
||||
boolean processSoftWraps();
|
||||
}
|
||||
+3
-9
@@ -45,7 +45,6 @@ public class SoftWrapHelper {
|
||||
* This method allows to answer if caret offset of the given editor points to soft wrap and visual caret position
|
||||
* belongs to the visual line where soft wrap end is located.
|
||||
*
|
||||
* @param editor target editor
|
||||
* @return <code>true</code> if caret offset of the given editor points to visual position that belongs to
|
||||
* visual line where soft wrap end is located
|
||||
*/
|
||||
@@ -61,13 +60,8 @@ public class SoftWrapHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editor.myUseNewRendering) {
|
||||
VisualPosition afterWrapPosition = editor.offsetToVisualPosition(offset, false, false);
|
||||
VisualPosition caretPosition = caret.getVisualPosition();
|
||||
return caretPosition.line == afterWrapPosition.line && caretPosition.column <= afterWrapPosition.column;
|
||||
}
|
||||
else {
|
||||
return editor.offsetToVisualLine(offset) == caret.getVisualPosition().line;
|
||||
}
|
||||
VisualPosition afterWrapPosition = editor.offsetToVisualPosition(offset, false, false);
|
||||
VisualPosition caretPosition = caret.getVisualPosition();
|
||||
return caretPosition.line == afterWrapPosition.line && caretPosition.column <= afterWrapPosition.column;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -129,17 +129,15 @@ public class SoftWrapsStorage implements Dumpable {
|
||||
* @param softWrap soft wrap to store
|
||||
* @return previous soft wrap object stored for the same offset if any; <code>null</code> otherwise
|
||||
*/
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
@Nullable
|
||||
public SoftWrap storeOrReplace(SoftWrapImpl softWrap) {
|
||||
public void storeOrReplace(SoftWrapImpl softWrap) {
|
||||
int i = getSoftWrapIndex(softWrap.getStart());
|
||||
if (i >= 0) {
|
||||
return myWraps.set(i, softWrap);
|
||||
myWraps.set(i, softWrap);
|
||||
return;
|
||||
}
|
||||
|
||||
i = -i - 1;
|
||||
myWraps.add(i, softWrap);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void remove(SoftWrapImpl softWrap) {
|
||||
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract super class for {@link DataProvider} implementations that use the same key all the time.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 12:19:32 PM
|
||||
*/
|
||||
public abstract class AbstractDataProvider<K extends Comparable<? super K>, V> implements DataProvider<K, V> {
|
||||
|
||||
private final K myKey;
|
||||
|
||||
public AbstractDataProvider(K key) {
|
||||
myKey = key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<K, V> getData() {
|
||||
V data = doGetData();
|
||||
return data == null ? null : Pair.create(myKey, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current data
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract V doGetData();
|
||||
}
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Generic {@link DataProvider} implementation that assumes that target data is available as list.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 12:56:58 PM
|
||||
*/
|
||||
public abstract class AbstractListBasedDataProvider<K extends Comparable<? super K>, V> extends AbstractDataProvider<K, V> {
|
||||
|
||||
private final List<V> myData;
|
||||
private int myIndex;
|
||||
|
||||
public AbstractListBasedDataProvider(@NotNull K key, @NotNull List<V> data) {
|
||||
super(key);
|
||||
|
||||
// We use given collection instead of copying it to private list because profiling indicates that as an expensive operation
|
||||
// when performed frequently.
|
||||
myData = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected V doGetData() {
|
||||
if (myIndex < myData.size()) {
|
||||
return myData.get(myIndex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
return ++myIndex < myData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance(int sortingKey) {
|
||||
|
||||
// We inline binary search here because profiling indicates that as a performance boost.
|
||||
int start = myIndex;
|
||||
int end = myData.size() - 1;
|
||||
|
||||
// We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
|
||||
while (start <= end) {
|
||||
int i = (end + start) >>> 1;
|
||||
V data = myData.get(i);
|
||||
if (getSortingKey(data) < sortingKey) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (getSortingKey(data) > sortingKey) {
|
||||
end = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
myIndex = i;
|
||||
return;
|
||||
}
|
||||
myIndex = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSortingKey() {
|
||||
if (myIndex < myData.size()) {
|
||||
return getSortingKey(myData.get(myIndex));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes are expected to be able to derive sorting key for the every data instance.
|
||||
*
|
||||
* @param data target data which sorting key should be returned
|
||||
* @return sorting key for the given data
|
||||
*/
|
||||
protected abstract int getSortingKey(@NotNull V data);
|
||||
}
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract super class for mapping strategies that encapsulates shared logic of advancing the context to the points
|
||||
* of specific types. I.e. it's main idea is to ask sub-class if target dimension lays inside particular region
|
||||
* (e.g. soft wrap, fold region etc) and use common algorithm for advancing context to the region's end in the case
|
||||
* of negative answer.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @param <T> resulting document dimension type
|
||||
*/
|
||||
abstract class AbstractMappingStrategy<T> implements MappingStrategy<T> {
|
||||
|
||||
protected final EditorEx myEditor;
|
||||
protected final SoftWrapsStorage myStorage;
|
||||
protected final List<CacheEntry> myCache;
|
||||
|
||||
private EditorPosition myInitialPosition;
|
||||
private CacheEntry myTargetEntry;
|
||||
private T myEagerMatch;
|
||||
private int myLastEntryOffset;
|
||||
|
||||
AbstractMappingStrategy(@NotNull EditorEx editor,
|
||||
@NotNull SoftWrapsStorage storage,
|
||||
@NotNull List<CacheEntry> cache)
|
||||
{
|
||||
myEditor = editor;
|
||||
myStorage = storage;
|
||||
myCache = cache;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T eagerMatch() {
|
||||
return myEagerMatch;
|
||||
}
|
||||
|
||||
protected void setEagerMatch(@Nullable T eagerMatch) {
|
||||
myEagerMatch = eagerMatch;
|
||||
}
|
||||
|
||||
protected void setFirstInitialPosition() {
|
||||
myInitialPosition = new EditorPosition(new LogicalPosition(0, 0), 0, myEditor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected CacheEntry getTargetEntry() {
|
||||
return myTargetEntry;
|
||||
}
|
||||
|
||||
protected void setTargetEntry(@NotNull CacheEntry targetEntry, boolean anchorToStart) {
|
||||
myTargetEntry = targetEntry;
|
||||
if (anchorToStart) {
|
||||
myInitialPosition = targetEntry.buildStartLinePosition();
|
||||
}
|
||||
else {
|
||||
myInitialPosition = targetEntry.buildEndLinePosition();
|
||||
}
|
||||
}
|
||||
|
||||
protected void reset() {
|
||||
myEagerMatch = null;
|
||||
myTargetEntry = null;
|
||||
myInitialPosition = null;
|
||||
if (!myCache.isEmpty()) {
|
||||
myLastEntryOffset = myCache.get(myCache.size() - 1).endOffset;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setInitialPosition(@NotNull EditorPosition position) {
|
||||
myInitialPosition = position;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public EditorPosition buildInitialPosition() {
|
||||
return myInitialPosition;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected FoldingData getFoldRegionData(@NotNull FoldRegion foldRegion) {
|
||||
int i = MappingUtil.getCacheEntryIndexForOffset(foldRegion.getStartOffset(), myEditor.getDocument(), myCache);
|
||||
if (i < 0 || i >= myCache.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheEntry cacheEntry = myCache.get(i);
|
||||
return cacheEntry.getFoldingData(foldRegion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T advance(@NotNull EditorPosition position, int offset) {
|
||||
Document document = myEditor.getDocument();
|
||||
if (offset >= myLastEntryOffset || offset >= document.getTextLength()) {
|
||||
return build(position);
|
||||
}
|
||||
|
||||
T result = buildIfExceeds(position, offset);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update context state and continue processing.
|
||||
int linesDiff = document.getLineNumber(offset) - position.logicalLine;
|
||||
position.logicalLine += linesDiff;
|
||||
position.visualLine += linesDiff;
|
||||
if (linesDiff <= 0) {
|
||||
int columnsDiff = offset - position.offset;
|
||||
position.visualColumn += columnsDiff;
|
||||
position.logicalColumn += columnsDiff;
|
||||
}
|
||||
else {
|
||||
int lineStartOffset = document.getLineStartOffset(position.logicalLine);
|
||||
int column = offset - lineStartOffset;
|
||||
position.visualColumn = column;
|
||||
position.logicalColumn = column;
|
||||
position.softWrapLinesBefore += position.softWrapLinesCurrent;
|
||||
position.softWrapLinesCurrent = 0;
|
||||
position.softWrapColumnDiff = 0;
|
||||
position.foldingColumnDiff = 0;
|
||||
}
|
||||
position.offset = offset;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract T buildIfExceeds(EditorPosition position, int offset);
|
||||
|
||||
@Override
|
||||
public T processFoldRegion(@NotNull EditorPosition position, @NotNull FoldRegion foldRegion) {
|
||||
T result = buildIfExceeds(position, foldRegion);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
advancePositionOnFolding(position, foldRegion);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void advancePositionOnFolding(@NotNull EditorPosition position, @NotNull FoldRegion foldRegion) {
|
||||
Document document = myEditor.getDocument();
|
||||
int endOffsetLogicalLine = document.getLineNumber(foldRegion.getEndOffset());
|
||||
if (position.logicalLine != endOffsetLogicalLine) {
|
||||
// Multi-line fold region.
|
||||
position.softWrapColumnDiff = 0;
|
||||
position.softWrapLinesBefore += position.softWrapLinesCurrent;
|
||||
position.softWrapLinesCurrent = 0;
|
||||
}
|
||||
|
||||
int collapsedSymbolsWidthInColumns = -1;
|
||||
FoldingData foldingData = getFoldRegionData(foldRegion);
|
||||
if (foldingData != null) {
|
||||
collapsedSymbolsWidthInColumns = foldingData.getCollapsedSymbolsWidthInColumns();
|
||||
}
|
||||
|
||||
position.advance(foldRegion, collapsedSymbolsWidthInColumns);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract T buildIfExceeds(@NotNull EditorPosition context, @NotNull FoldRegion foldRegion);
|
||||
|
||||
@Override
|
||||
public T processTabulation(@NotNull EditorPosition position, TabData tabData) {
|
||||
T result = buildIfExceeds(position, tabData);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
position.visualColumn += tabData.widthInColumns;
|
||||
position.logicalColumn += tabData.widthInColumns;
|
||||
position.offset++;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract T buildIfExceeds(EditorPosition context, TabData tabData);
|
||||
}
|
||||
-278
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import gnu.trove.TObjectProcedure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Encapsulates information to cache for the single visual line.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
class CacheEntry implements Comparable<CacheEntry>, Cloneable {
|
||||
|
||||
private static final TIntObjectHashMap<FoldingData> DUMMY = new TIntObjectHashMap<>();
|
||||
|
||||
public int visualLine;
|
||||
|
||||
public int startLogicalLine;
|
||||
public int startLogicalColumn;
|
||||
public int startOffset;
|
||||
public int startSoftWrapLinesBefore;
|
||||
public int startSoftWrapLinesCurrent;
|
||||
public int startSoftWrapColumnDiff;
|
||||
public int startFoldedLines;
|
||||
public int startFoldingColumnDiff;
|
||||
|
||||
public int endOffset;
|
||||
public int endLogicalLine;
|
||||
public int endLogicalColumn;
|
||||
public int endVisualColumn;
|
||||
public int endSoftWrapLinesBefore;
|
||||
public int endSoftWrapLinesCurrent;
|
||||
public int endSoftWrapColumnDiff;
|
||||
public int endFoldedLines;
|
||||
public int endFoldingColumnDiff;
|
||||
|
||||
private final Editor myEditor;
|
||||
|
||||
/** Holds positions for the tabulation symbols on a target visual line sorted by offset in ascending order. */
|
||||
private List<TabData> myTabPositions = Collections.EMPTY_LIST;
|
||||
|
||||
/** Holds information about single line fold regions representation data. */
|
||||
private TIntObjectHashMap<FoldingData> myFoldingData = DUMMY;
|
||||
|
||||
CacheEntry(int visualLine, @NotNull Editor editor) {
|
||||
this.visualLine = visualLine;
|
||||
myEditor = editor;
|
||||
}
|
||||
|
||||
public void setLineStartPosition(@NotNull EditorPosition context) {
|
||||
assert context.visualColumn == 0;
|
||||
startLogicalLine = context.logicalLine;
|
||||
startLogicalColumn = context.logicalColumn;
|
||||
visualLine = context.visualLine;
|
||||
startOffset = context.offset;
|
||||
startSoftWrapLinesBefore = context.softWrapLinesBefore;
|
||||
startSoftWrapLinesCurrent = context.softWrapLinesCurrent;
|
||||
startSoftWrapColumnDiff = context.softWrapColumnDiff;
|
||||
startFoldedLines = context.foldedLines;
|
||||
startFoldingColumnDiff = context.foldingColumnDiff;
|
||||
}
|
||||
|
||||
public void setLineEndPosition(@NotNull EditorPosition position) {
|
||||
endOffset = position.offset;
|
||||
endLogicalLine = position.logicalLine;
|
||||
endLogicalColumn = position.logicalColumn;
|
||||
endVisualColumn = position.visualColumn;
|
||||
endSoftWrapLinesBefore = position.softWrapLinesBefore;
|
||||
endSoftWrapLinesCurrent = position.softWrapLinesCurrent;
|
||||
endSoftWrapColumnDiff = position.softWrapColumnDiff;
|
||||
endFoldedLines = position.foldedLines;
|
||||
endFoldingColumnDiff = position.foldingColumnDiff;
|
||||
}
|
||||
|
||||
public EditorPosition buildStartLinePosition() {
|
||||
EditorPosition result = new EditorPosition(myEditor);
|
||||
result.logicalLine = startLogicalLine;
|
||||
result.logicalColumn = startLogicalColumn;
|
||||
result.offset = startOffset;
|
||||
result.visualLine = visualLine;
|
||||
result.visualColumn = 0;
|
||||
result.softWrapLinesBefore = startSoftWrapLinesBefore;
|
||||
result.softWrapLinesCurrent = startSoftWrapLinesCurrent;
|
||||
result.softWrapColumnDiff = startSoftWrapColumnDiff;
|
||||
result.foldedLines = startFoldedLines;
|
||||
result.foldingColumnDiff = startFoldingColumnDiff;
|
||||
return result;
|
||||
}
|
||||
|
||||
public EditorPosition buildEndLinePosition() {
|
||||
EditorPosition result = new EditorPosition(myEditor);
|
||||
result.logicalLine = endLogicalLine;
|
||||
result.logicalColumn = endLogicalColumn;
|
||||
result.offset = endOffset;
|
||||
result.visualLine = visualLine;
|
||||
result.visualColumn = endVisualColumn;
|
||||
result.softWrapLinesBefore = endSoftWrapLinesBefore;
|
||||
result.softWrapLinesCurrent = endSoftWrapLinesCurrent;
|
||||
result.softWrapColumnDiff = endSoftWrapColumnDiff;
|
||||
result.foldedLines = endFoldedLines;
|
||||
result.foldingColumnDiff = endFoldingColumnDiff;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes data for all tabs and fold regions that start at or after the given offset.
|
||||
*
|
||||
* @param offset target offset
|
||||
*/
|
||||
public void removeAllDataAtOrAfter(final int offset) {
|
||||
if (myFoldingData != DUMMY && !myFoldingData.isEmpty()) {
|
||||
myFoldingData.retainEntries(new TIntObjectProcedure<FoldingData>() {
|
||||
@Override
|
||||
public boolean execute(int a, FoldingData b) {
|
||||
return a < offset;
|
||||
}
|
||||
});
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < myTabPositions.size(); i++) {
|
||||
if (myTabPositions.get(i).offset >= offset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
myTabPositions.subList(i, myTabPositions.size()).clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FoldingData getFoldingData(@NotNull final FoldRegion region) {
|
||||
FoldingData candidate = myFoldingData.get(region.getStartOffset());
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
// Folding implementation is known to postpone actual fold region offsets update on document change, i.e. it performs
|
||||
// fold data caching with its further replace by up-to-date info. Hence, there is a possible case that soft wraps processing
|
||||
// advances fold region offset but folding model still provides old cached values. Hence, we're trying to match exact given
|
||||
// fold region against the cached data here.
|
||||
final Ref<FoldingData> result = new Ref<>();
|
||||
myFoldingData.forEachValue(new TObjectProcedure<FoldingData>() {
|
||||
@Override
|
||||
public boolean execute(FoldingData data) {
|
||||
if (data.getFoldRegion().equals(region)) {
|
||||
result.set(data);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public void store(FoldingData foldData, int offset) {
|
||||
if (myFoldingData == DUMMY) {
|
||||
myFoldingData = new TIntObjectHashMap<>();
|
||||
}
|
||||
myFoldingData.put(offset, foldData);
|
||||
}
|
||||
|
||||
public List<TabData> getTabData() {
|
||||
return myTabPositions;
|
||||
}
|
||||
|
||||
public void storeTabData(TabData tabData) {
|
||||
if (myTabPositions == Collections.EMPTY_LIST) {
|
||||
myTabPositions = new ArrayList<>();
|
||||
}
|
||||
myTabPositions.add(tabData);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
||||
public void advance(final int offsetDiff) {
|
||||
startOffset += offsetDiff;
|
||||
endOffset += offsetDiff;
|
||||
|
||||
// 'For-each' loop is not used here because this code is called quite often and profile shows the Iterator usage here
|
||||
// produces performance drawback.
|
||||
for (int i = 0; i < myTabPositions.size(); i++) {
|
||||
myTabPositions.get(i).offset += offsetDiff;
|
||||
}
|
||||
|
||||
if (myFoldingData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final TIntObjectHashMap<FoldingData> newFoldingData = new TIntObjectHashMap<>(myFoldingData.size());
|
||||
myFoldingData.forEachEntry(new TIntObjectProcedure<FoldingData>() {
|
||||
@Override
|
||||
public boolean execute(int offset, FoldingData foldingData) {
|
||||
newFoldingData.put(offset + offsetDiff, foldingData);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
myFoldingData = newFoldingData;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public TIntObjectHashMap<FoldingData> getFoldingData() {
|
||||
return myFoldingData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CacheEntry e) {
|
||||
return visualLine - e.visualLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%d - visual line: %d, offsets: %d-%d, logical lines: %d-%d, logical columns: %d-%d, end visual column: %d, "
|
||||
+ "fold regions: %s, tab data: %s",
|
||||
System.identityHashCode(this), visualLine, startOffset, endOffset, startLogicalLine, endLogicalLine, startLogicalColumn,
|
||||
endLogicalColumn, endVisualColumn, Arrays.toString(myFoldingData.getValues()), myTabPositions
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheEntry clone() {
|
||||
final CacheEntry result = new CacheEntry(visualLine, myEditor);
|
||||
|
||||
result.startLogicalLine = startLogicalLine;
|
||||
result.startLogicalColumn = startLogicalColumn;
|
||||
result.startOffset = startOffset;
|
||||
result.startSoftWrapLinesBefore = startSoftWrapLinesBefore;
|
||||
result.startSoftWrapLinesCurrent = startSoftWrapLinesCurrent;
|
||||
result.startSoftWrapColumnDiff = startSoftWrapColumnDiff;
|
||||
result.startFoldedLines = startFoldedLines;
|
||||
result.startFoldingColumnDiff = startFoldingColumnDiff;
|
||||
|
||||
result.endOffset = endOffset;
|
||||
result.endLogicalLine = endLogicalLine;
|
||||
result.endLogicalColumn = endLogicalColumn;
|
||||
result.endVisualColumn = endVisualColumn;
|
||||
result.endSoftWrapLinesBefore = endSoftWrapLinesBefore;
|
||||
result.endSoftWrapLinesCurrent = endSoftWrapLinesCurrent;
|
||||
result.endSoftWrapColumnDiff = endSoftWrapColumnDiff;
|
||||
result.endFoldedLines = endFoldedLines;
|
||||
result.endFoldingColumnDiff = endFoldingColumnDiff;
|
||||
|
||||
myFoldingData.forEachEntry(new TIntObjectProcedure<FoldingData>() {
|
||||
@Override
|
||||
public boolean execute(int offset, FoldingData foldData) {
|
||||
result.store(foldData, offset);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
for (TabData tabPosition : myTabPositions) {
|
||||
result.storeTabData(tabPosition);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+3
-573
@@ -19,117 +19,30 @@ import com.intellij.diagnostic.Dumpable;
|
||||
import com.intellij.openapi.diagnostic.Attachment;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.FoldingModelEx;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
|
||||
import com.intellij.openapi.editor.impl.TextChangeImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Every document that is exposed to end-user via IJ editor has a number of various dimensions ({@link LogicalPosition logical}
|
||||
* and {@link VisualPosition visual} positions, {@link Document#getCharsSequence() text offset} etc. It's very important to be
|
||||
* able to map one dimension to another and do that effectively.
|
||||
*
|
||||
* This class is implemented using the following principles:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>it caches document dimension for starts of all visual lines;</li>
|
||||
* <li>
|
||||
* every time document position mapping should be performed this mapper chooses target visual line to process
|
||||
* and calculates target position from its start (note that we can optimize that in order to choose calculation
|
||||
* direction - 'from start' or 'from end');
|
||||
* </li>
|
||||
* <li>
|
||||
* information about target visual lines is updated incrementally on document changes, i.e. we're trying to reduce
|
||||
* calculations number as much as possible;
|
||||
* </li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 10:24:47 AM
|
||||
*/
|
||||
public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingListener, Dumpable {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#" + CachingSoftWrapDataMapper.class.getName());
|
||||
|
||||
/** Caches information for the document visual line starts sorted in ascending order. */
|
||||
private final List<CacheEntry> myCache = new ArrayList<>();
|
||||
private final List<CacheEntry> myAffectedByUpdateCacheEntries = new ArrayList<>();
|
||||
private final List<SoftWrapImpl> myAffectedByUpdateSoftWraps = new ArrayList<>();
|
||||
|
||||
private final LogicalToOffsetCalculationStrategy myLogicalToOffsetStrategy;
|
||||
private final OffsetToLogicalCalculationStrategy myOffsetToLogicalStrategy;
|
||||
private final VisualToLogicalCalculationStrategy myVisualToLogicalStrategy;
|
||||
private final EditorEx myEditor;
|
||||
private final SoftWrapsStorage myStorage;
|
||||
private final CacheEntry mySearchKey;
|
||||
|
||||
public CachingSoftWrapDataMapper(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage)
|
||||
{
|
||||
myEditor = editor;
|
||||
myStorage = storage;
|
||||
mySearchKey = new CacheEntry(0, editor);
|
||||
|
||||
myLogicalToOffsetStrategy = new LogicalToOffsetCalculationStrategy(editor, storage, myCache);
|
||||
myOffsetToLogicalStrategy = new OffsetToLogicalCalculationStrategy(editor, storage, myCache);
|
||||
myVisualToLogicalStrategy = new VisualToLogicalCalculationStrategy(editor, storage, myCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps given visual position to corresponding logical.
|
||||
*
|
||||
* @param visual visual position to map
|
||||
* @return logical position that corresponds to the given visual position
|
||||
* @throws IllegalStateException if it's not possible to perform a mapping
|
||||
*/
|
||||
@NotNull
|
||||
public LogicalPosition visualToLogical(@NotNull VisualPosition visual) throws IllegalStateException {
|
||||
if (myCache.isEmpty()) {
|
||||
return new LogicalPosition(visual.line, visual.column, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
myVisualToLogicalStrategy.init(visual, myCache);
|
||||
return calculate(myVisualToLogicalStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps given offset to corresponding logical position.
|
||||
*
|
||||
* @param offset offset to map
|
||||
* @return logical position that corresponds to the given offset
|
||||
* @throws IllegalStateException if it's not possible to perform a mapping
|
||||
*/
|
||||
@NotNull
|
||||
public LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
myOffsetToLogicalStrategy.init(offset, myCache);
|
||||
return calculate(myOffsetToLogicalStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps given logical position to corresponding offset.
|
||||
*
|
||||
* @param logical logical position to map
|
||||
* @return offset that corresponds to the given logical position
|
||||
* @throws IllegalStateException if it's not possible to perform a mapping
|
||||
*/
|
||||
public int logicalPositionToOffset(@NotNull LogicalPosition logical) throws IllegalStateException {
|
||||
myLogicalToOffsetStrategy.init(logical);
|
||||
return calculate(myLogicalToOffsetStrategy);
|
||||
}
|
||||
|
||||
boolean matchesOldSoftWrap(SoftWrap newSoftWrap, int lengthDiff) {
|
||||
@@ -155,354 +68,25 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi
|
||||
}) >= 0;
|
||||
}
|
||||
|
||||
public int getVisualLineStartOffset(int visualLine) {
|
||||
int index = getCacheEntryIndexForVisualLine(visualLine);
|
||||
if (index >= 0) {
|
||||
return myCache.get(index).startOffset;
|
||||
}
|
||||
else {
|
||||
int previousIndex = - index - 2;
|
||||
if (previousIndex < 0) {
|
||||
return getLogicalLineStartOffset(visualLine);
|
||||
}
|
||||
else {
|
||||
CacheEntry prevEntry = myCache.get(previousIndex);
|
||||
return getLogicalLineStartOffset(prevEntry.endLogicalLine + visualLine - prevEntry.visualLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getLogicalLineStartOffset(int logicalLine) {
|
||||
DocumentEx document = myEditor.getDocument();
|
||||
return logicalLine >= document.getLineCount() ? document.getTextLength() : document.getLineStartOffset(logicalLine);
|
||||
}
|
||||
|
||||
public int getPreviousVisualLineStartOffset(int offset) {
|
||||
Document document = myEditor.getDocument();
|
||||
int cacheIndex = MappingUtil.getCacheEntryIndexForOffset(offset, document, myCache);
|
||||
if (cacheIndex < 0) {
|
||||
return document.getLineStartOffset(document.getLineNumber(offset));
|
||||
}
|
||||
else if (cacheIndex == 0) {
|
||||
return myCache.get(cacheIndex).startOffset;
|
||||
}
|
||||
else {
|
||||
CacheEntry thisEntry = myCache.get(cacheIndex);
|
||||
CacheEntry prevEntry = myCache.get(cacheIndex - 1);
|
||||
return thisEntry.startOffset == prevEntry.endOffset ? prevEntry.startOffset : thisEntry.startOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps given logical position to corresponding visual position.
|
||||
*
|
||||
* @param logical logical position to map
|
||||
* @param softWrapUnawareVisual soft wrap-unaware visual position that corresponds to the given logical position
|
||||
* @return visual position that corresponds to the given logical position
|
||||
* @throws IllegalStateException if it's not possible to perform a mapping
|
||||
*/
|
||||
public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition softWrapUnawareVisual)
|
||||
throws IllegalStateException
|
||||
{
|
||||
// We can't use standard strategy-based approach with logical -> visual mapping because folding processing quite often
|
||||
// temporarily disables folding. So, there is an inconsistency between cached data (folding aware) and current folding
|
||||
// state. So, we use direct soft wraps adjustment instead of normal calculation.
|
||||
|
||||
|
||||
if (logical.visualPositionAware) {
|
||||
// We don't need to recalculate logical position adjustments because given object already has them.
|
||||
return logical.toVisualPosition();
|
||||
}
|
||||
|
||||
List<? extends SoftWrap> softWraps = myStorage.getSoftWraps();
|
||||
|
||||
// Check if there are registered soft wraps before the target logical position.
|
||||
int maxOffset = myEditor.logicalPositionToOffset(logical);
|
||||
int endIndex = myStorage.getSoftWrapIndex(maxOffset);
|
||||
if (endIndex < 0) {
|
||||
endIndex = -endIndex - 2; // We subtract '2' instead of '1' here in order to point to offset of the first soft wrap that
|
||||
// is located before the given logical position.
|
||||
}
|
||||
|
||||
// Return eagerly if no soft wraps are registered before the target offset.
|
||||
if (endIndex < 0 || endIndex >= softWraps.size()) {
|
||||
return softWrapUnawareVisual;
|
||||
}
|
||||
|
||||
int lineDiff = 0;
|
||||
int column = -1;
|
||||
|
||||
int targetLogicalLineStartOffset = myEditor.logicalPositionToOffset(new LogicalPosition(logical.line, 0));
|
||||
for (int i = endIndex; i >= 0; i--) {
|
||||
SoftWrap softWrap = softWraps.get(i);
|
||||
if (softWrap == null) {
|
||||
assert false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Count soft wrap column offset only if it's located at the same line as the target offset.
|
||||
if (column < 0 && softWrap.getStart() >= targetLogicalLineStartOffset) {
|
||||
column = softWrap.getIndentInColumns() + SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor).toVisualColumnSymbolsNumber(
|
||||
softWrap.getStart(), maxOffset, softWrap.getIndentInPixels()
|
||||
);
|
||||
|
||||
// Count lines introduced by the current soft wrap. We assume that every soft wrap has a single line feed.
|
||||
lineDiff++;
|
||||
}
|
||||
else {
|
||||
// We assume that every soft wrap has a single line feed.
|
||||
lineDiff += i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int columnToUse = column >= 0 ? column : softWrapUnawareVisual.column;
|
||||
return new VisualPosition(softWrapUnawareVisual.line + lineDiff, columnToUse);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
myCache.clear();
|
||||
}
|
||||
|
||||
private <T> T calculate(@NotNull MappingStrategy<T> strategy) throws IllegalStateException {
|
||||
T eagerMatch = strategy.eagerMatch();
|
||||
if (eagerMatch != null) {
|
||||
return eagerMatch;
|
||||
}
|
||||
|
||||
EditorPosition position = strategy.buildInitialPosition();
|
||||
|
||||
// Folding model doesn't return information about fold regions if their 'enabled' state is set to 'false'. Unfortunately,
|
||||
// such situation occurs not only on manual folding disabling but on internal processing as well. E.g. 'enabled' is set
|
||||
// to 'false' during fold preview representation. So, we set it to 'true' in order to retrieve fold regions and restore
|
||||
// to previous state after that.
|
||||
FoldingModelEx foldingModel = myEditor.getFoldingModel();
|
||||
boolean foldingState = foldingModel.isFoldingEnabled();
|
||||
foldingModel.setFoldingEnabled(true);
|
||||
CompositeDataProvider provider;
|
||||
try {
|
||||
provider = new CompositeDataProvider(
|
||||
new SoftWrapsDataProvider(myStorage), new FoldingDataProvider(myEditor.getFoldingModel().fetchTopLevel()),
|
||||
getTabulationDataProvider(position.visualLine)
|
||||
);
|
||||
}
|
||||
finally {
|
||||
foldingModel.setFoldingEnabled(foldingState);
|
||||
}
|
||||
provider.advance(position.offset);
|
||||
|
||||
while (provider.hasData()) {
|
||||
Pair<SoftWrapDataProviderKeys, ?> data = provider.getData();
|
||||
T result = null;
|
||||
int sortingKey = provider.getSortingKey();
|
||||
|
||||
// There is a possible case that, say, fold region is soft wrapped. We don't want to perform unnecessary then.
|
||||
if (position.offset <= sortingKey) {
|
||||
result = strategy.advance(position, sortingKey);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
switch (data.first) {
|
||||
case SOFT_WRAP: result = strategy.processSoftWrap(position, (SoftWrap)data.second); break;
|
||||
case COLLAPSED_FOLDING: result = strategy.processFoldRegion(position, (FoldRegion)data.second); break;
|
||||
case TABULATION: result = strategy.processTabulation(position, (TabData)data.second); break;
|
||||
}
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
provider.next();
|
||||
}
|
||||
return strategy.build(position);
|
||||
}
|
||||
|
||||
private TabulationDataProvider getTabulationDataProvider(int visualLine) throws IllegalStateException {
|
||||
mySearchKey.visualLine = visualLine;
|
||||
int i = Collections.binarySearch(myCache, mySearchKey);
|
||||
List<TabData> tabs;
|
||||
if (i >= 0) {
|
||||
tabs = myCache.get(i).getTabData();
|
||||
}
|
||||
else {
|
||||
tabs = Collections.emptyList();
|
||||
}
|
||||
return new TabulationDataProvider(tabs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisualLineStart(@NotNull EditorPosition position) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry cacheEntry = getCacheEntryForVisualLine(position.visualLine, true);
|
||||
if (cacheEntry == null) {
|
||||
return;
|
||||
}
|
||||
cacheEntry.setLineStartPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisualLineEnd(@NotNull EditorPosition position) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry cacheEntry = getCacheEntryForVisualLine(position.visualLine, false);
|
||||
if (cacheEntry == null) {
|
||||
return;
|
||||
}
|
||||
cacheEntry.setLineEndPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCollapsedFoldRegion(@NotNull FoldRegion foldRegion, int widthInColumns, int visualLine) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry cacheEntry = getCacheEntryForVisualLine(visualLine, false);
|
||||
if (cacheEntry == null) {
|
||||
return;
|
||||
}
|
||||
cacheEntry.store(new FoldingData(foldRegion, widthInColumns), foldRegion.getStartOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSoftWrapLineFeed(@NotNull EditorPosition position) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry cacheEntry = getCacheEntryForVisualLine(position.visualLine, false);
|
||||
if (cacheEntry == null) {
|
||||
return;
|
||||
}
|
||||
cacheEntry.setLineEndPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSoftWrapLineFeed(@NotNull EditorPosition position) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry cacheEntry = getCacheEntryForVisualLine(position.visualLine, true);
|
||||
if (cacheEntry == null) {
|
||||
return;
|
||||
}
|
||||
cacheEntry.setLineStartPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revertToOffset(final int offset, int visualLine) {
|
||||
if (isNewRendering()) return;
|
||||
final CacheEntry entry = getCacheEntryForVisualLine(visualLine, false);
|
||||
if (entry != null) {
|
||||
entry.removeAllDataAtOrAfter(offset);
|
||||
}
|
||||
// Do nothing more in assumption that we store only information about start and end visual line positions and
|
||||
// that start information remains the same and end of line is not reached yet.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabulation(@NotNull EditorPosition position, int widthInColumns) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry cacheEntry = getCacheEntryForVisualLine(position.visualLine, false);
|
||||
if (cacheEntry == null) {
|
||||
return;
|
||||
}
|
||||
cacheEntry.storeTabData(new TabData(widthInColumns, position.offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculationEnds() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to retrieve {@link CacheEntry} object that stores data for the given visual line.
|
||||
* <p/>
|
||||
* There is a possible case that no such object is registered yet and it may be created if necessary as a result of this
|
||||
* method call.
|
||||
*
|
||||
* @param visualLine target visual line
|
||||
* @param createIfNecessary flag that indicates if new {@link CacheEntry} object should be created in case no information
|
||||
* is stored for the target visual line
|
||||
* @return {@link CacheEntry} object that stores cache data for the target visual line if any;
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
private CacheEntry getCacheEntryForVisualLine(int visualLine, boolean createIfNecessary) {
|
||||
// Blind guess, worth to perform in assumption that this method is called on document parsing most of the time.
|
||||
if (!myCache.isEmpty()) {
|
||||
CacheEntry lastEntry = myCache.get(myCache.size() - 1);
|
||||
if (lastEntry.visualLine == visualLine) {
|
||||
return lastEntry;
|
||||
}
|
||||
else if (lastEntry.visualLine < visualLine && createIfNecessary) {
|
||||
CacheEntry result = new CacheEntry(visualLine, myEditor);
|
||||
myCache.add(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int cacheEntryIndex = getCacheEntryIndexForVisualLine(visualLine);
|
||||
|
||||
CacheEntry result = null;
|
||||
if (cacheEntryIndex < 0) {
|
||||
cacheEntryIndex = - cacheEntryIndex - 1;
|
||||
if (createIfNecessary) {
|
||||
myCache.add(cacheEntryIndex, result = new CacheEntry(visualLine, myEditor));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = myCache.get(cacheEntryIndex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getCacheEntryIndexForVisualLine(int visualLine) {
|
||||
int start = 0;
|
||||
int end = myCache.size() - 1;
|
||||
int cacheEntryIndex = -1;
|
||||
|
||||
// We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
|
||||
while (start <= end) {
|
||||
int i = (end + start) >>> 1;
|
||||
CacheEntry cacheEntry = myCache.get(i);
|
||||
if (cacheEntry.visualLine < visualLine) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (cacheEntry.visualLine > visualLine) {
|
||||
end = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
cacheEntryIndex = i;
|
||||
break;
|
||||
}
|
||||
return cacheEntryIndex < 0 ? (- start - 1) : cacheEntryIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCacheUpdateStart(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
int startOffset = event.getStartOffset();
|
||||
|
||||
if (!isNewRendering()) {
|
||||
int startIndex = MappingUtil.getCacheEntryIndexForOffset(startOffset, myEditor.getDocument(), myCache);
|
||||
if (startIndex < 0) {
|
||||
startIndex = - startIndex - 1;
|
||||
}
|
||||
|
||||
myAffectedByUpdateCacheEntries.clear();
|
||||
if (startIndex < myCache.size()) {
|
||||
List<CacheEntry> affectedEntries = myCache.subList(startIndex, myCache.size());
|
||||
myAffectedByUpdateCacheEntries.addAll(affectedEntries);
|
||||
affectedEntries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
myAffectedByUpdateSoftWraps.clear();
|
||||
myAffectedByUpdateSoftWraps.addAll(myStorage.removeStartingFrom(startOffset + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecalculationEnd(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
int softWrappedLinesDiff = advanceSoftWrapOffsets(event);
|
||||
applyStateChange(event, softWrappedLinesDiff);
|
||||
advanceSoftWrapOffsets(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
myCache.clear();
|
||||
myAffectedByUpdateCacheEntries.clear();
|
||||
myAffectedByUpdateSoftWraps.clear();
|
||||
}
|
||||
|
||||
@@ -511,7 +95,7 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi
|
||||
*
|
||||
* @return Change in soft wraps count after recalculation
|
||||
*/
|
||||
private int advanceSoftWrapOffsets(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
private void advanceSoftWrapOffsets(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
int lengthDiff = event.getLengthDiff();
|
||||
int recalcEndOffsetTranslated = event.getActualEndOffset() - lengthDiff;
|
||||
|
||||
@@ -547,102 +131,8 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi
|
||||
if (softWrapsChanged) {
|
||||
myStorage.notifyListenersAboutChange();
|
||||
}
|
||||
return softWrappedLinesDiff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which cache entries were not affected by recalculation, and 'shifts' them according to recalculation results.
|
||||
*/
|
||||
private void applyStateChange(@NotNull IncrementalCacheUpdateEvent event, int softWrappedLinesDiff) {
|
||||
if (isNewRendering()) return;
|
||||
CacheEntry lastEntry = myCache.isEmpty() ? null : myCache.get(myCache.size() - 1);
|
||||
int lengthDiff = event.getLengthDiff();
|
||||
int recalcEndOffsetTranslated = event.getActualEndOffset() - lengthDiff;
|
||||
|
||||
int startIndex = MappingUtil.getCacheEntryIndexForOffset(event.getStartOffset(), myEditor.getDocument(), myCache);
|
||||
if (startIndex < 0) {
|
||||
startIndex = -startIndex - 1;
|
||||
}
|
||||
int foldedLinesDiff = 0;
|
||||
for (int i = startIndex; i < myCache.size(); i++) {
|
||||
CacheEntry entry = myCache.get(i);
|
||||
foldedLinesDiff += entry.endFoldedLines - entry.startFoldedLines;
|
||||
}
|
||||
|
||||
int logicalLinesDiff = event.getLogicalLinesDiff();
|
||||
int firstIndex = -1;
|
||||
int borderLogicalLine = lastEntry == null ? -1 : lastEntry.endLogicalLine;
|
||||
int borderColumnDiff = 0;
|
||||
int borderFoldedColumnDiff = 0;
|
||||
int borderSoftWrapColumnDiff = 0;
|
||||
int borderSoftWrapLinesBeforeDiff = 0;
|
||||
int borderSoftWrapLinesCurrentDiff = 0;
|
||||
int affectedEntriesCount = myAffectedByUpdateCacheEntries.size();
|
||||
for (int i = 0; i < affectedEntriesCount; i++) {
|
||||
CacheEntry entry = myAffectedByUpdateCacheEntries.get(i);
|
||||
if (firstIndex < 0) {
|
||||
if (entry.startOffset < recalcEndOffsetTranslated) {
|
||||
foldedLinesDiff -= entry.endFoldedLines - entry.startFoldedLines;
|
||||
continue;
|
||||
}
|
||||
firstIndex = i;
|
||||
if (lastEntry != null && entry.startLogicalLine + logicalLinesDiff == borderLogicalLine) {
|
||||
borderColumnDiff = lastEntry.endLogicalColumn - entry.startLogicalColumn;
|
||||
borderSoftWrapLinesBeforeDiff = lastEntry.endSoftWrapLinesBefore - entry.startSoftWrapLinesBefore;
|
||||
borderSoftWrapLinesCurrentDiff = lastEntry.endSoftWrapLinesCurrent - entry.startSoftWrapLinesCurrent + 1;
|
||||
borderFoldedColumnDiff = lastEntry.endFoldingColumnDiff - entry.startFoldingColumnDiff;
|
||||
borderSoftWrapColumnDiff = -borderColumnDiff - borderFoldedColumnDiff;
|
||||
}
|
||||
if (lengthDiff == 0 && logicalLinesDiff == 0 && foldedLinesDiff == 0 && softWrappedLinesDiff == 0
|
||||
&& borderColumnDiff == 0 && borderSoftWrapColumnDiff == 0 && borderFoldedColumnDiff == 0
|
||||
&& borderSoftWrapLinesBeforeDiff == 0 && borderSoftWrapLinesCurrentDiff == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstIndex >= 0 && i >= firstIndex) {
|
||||
entry.visualLine += (logicalLinesDiff + softWrappedLinesDiff - foldedLinesDiff);
|
||||
entry.startLogicalLine += logicalLinesDiff;
|
||||
entry.endLogicalLine += logicalLinesDiff;
|
||||
entry.advance(lengthDiff);
|
||||
entry.startFoldedLines += foldedLinesDiff;
|
||||
entry.endFoldedLines += foldedLinesDiff;
|
||||
if (entry.startLogicalLine == borderLogicalLine) {
|
||||
entry.startLogicalColumn += borderColumnDiff;
|
||||
entry.startSoftWrapLinesBefore += borderSoftWrapLinesBeforeDiff;
|
||||
entry.startSoftWrapLinesCurrent += borderSoftWrapLinesCurrentDiff;
|
||||
entry.startSoftWrapColumnDiff += borderSoftWrapColumnDiff;
|
||||
entry.startFoldingColumnDiff += borderFoldedColumnDiff;
|
||||
}
|
||||
else {
|
||||
entry.startSoftWrapLinesBefore += softWrappedLinesDiff;
|
||||
}
|
||||
if (entry.endLogicalLine == borderLogicalLine) {
|
||||
entry.endLogicalColumn += borderColumnDiff;
|
||||
entry.endSoftWrapLinesBefore += borderSoftWrapLinesBeforeDiff;
|
||||
entry.endSoftWrapLinesCurrent += borderSoftWrapLinesCurrentDiff;
|
||||
entry.endSoftWrapColumnDiff += borderSoftWrapColumnDiff;
|
||||
entry.endFoldingColumnDiff += borderFoldedColumnDiff;
|
||||
}
|
||||
else {
|
||||
entry.endSoftWrapLinesBefore += softWrappedLinesDiff;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstIndex >= 0) {
|
||||
if (lastEntry != null) {
|
||||
CacheEntry nextEntry = myAffectedByUpdateCacheEntries.get(firstIndex);
|
||||
if (lastEntry.visualLine >= nextEntry.visualLine) {
|
||||
LOG.error("Invalid soft wrap cache update", new Attachment("state.txt", myEditor.getSoftWrapModel().toString()));
|
||||
}
|
||||
}
|
||||
if (myAffectedByUpdateCacheEntries.get(affectedEntriesCount - 1).endOffset > myEditor.getDocument().getTextLength()) {
|
||||
LOG.error("Invalid soft wrap cache entries emerged", new Attachment("state.txt", myEditor.getSoftWrapModel().toString()));
|
||||
}
|
||||
myCache.addAll(myAffectedByUpdateCacheEntries.subList(firstIndex, myAffectedByUpdateCacheEntries.size()));
|
||||
}
|
||||
myAffectedByUpdateCacheEntries.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SoftWrapImpl getLastSoftWrap() {
|
||||
List<SoftWrapImpl> softWraps = myStorage.getSoftWraps();
|
||||
@@ -652,71 +142,11 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi
|
||||
@NotNull
|
||||
@Override
|
||||
public String dumpState() {
|
||||
return "Current cache: " + myCache.toString() + ", entries affected by current update: " + myAffectedByUpdateCacheEntries
|
||||
+ ", soft wraps affected by current update: " + myAffectedByUpdateSoftWraps;
|
||||
return "Soft wraps affected by current update: " + myAffectedByUpdateSoftWraps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return dumpState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to register new entry with the given data at the soft wraps cache.
|
||||
* <p/>
|
||||
* One entry is expected to contain information about single visual lines (what logical lines are mapped to it, what fold
|
||||
* regions and tabulations are located there etc).
|
||||
*
|
||||
* @param visualLine target entry's visual line
|
||||
* @param startOffset target entry's start offset
|
||||
* @param endOffset target entry's end offset
|
||||
* @param foldRegions target entry's fold regions
|
||||
* @param tabData target entry's tab data
|
||||
*/
|
||||
@TestOnly
|
||||
public void rawAdd(int visualLine,
|
||||
int startOffset,
|
||||
int endOffset,
|
||||
int startLogicalLine,
|
||||
int startLogicalColumn,
|
||||
int endLogicalLine,
|
||||
int endLogicalColumn,
|
||||
int endVisualColumn,
|
||||
@NotNull List<Pair<Integer, FoldRegion>> foldRegions,
|
||||
@NotNull List<Pair<Integer, Integer>> tabData)
|
||||
{
|
||||
final CacheEntry entry = new CacheEntry(visualLine, myEditor);
|
||||
entry.startOffset = startOffset;
|
||||
entry.endOffset = endOffset;
|
||||
entry.startLogicalLine = startLogicalLine;
|
||||
assert startLogicalLine == myEditor.getDocument().getLineNumber(startOffset);
|
||||
entry.startLogicalColumn = startLogicalColumn;
|
||||
entry.endLogicalLine = endLogicalLine;
|
||||
assert endLogicalLine == myEditor.getDocument().getLineNumber(endOffset);
|
||||
entry.endLogicalColumn = endLogicalColumn;
|
||||
entry.endVisualColumn = endVisualColumn;
|
||||
for (Pair<Integer, FoldRegion> region : foldRegions) {
|
||||
final FoldingData foldData = new FoldingData(region.second, region.first);
|
||||
entry.store(foldData, region.second.getStartOffset());
|
||||
}
|
||||
for (Pair<Integer, Integer> pair : tabData) {
|
||||
entry.storeTabData(new TabData(pair.second, pair.first));
|
||||
}
|
||||
myCache.add(entry);
|
||||
}
|
||||
|
||||
void removeLastCacheEntry() {
|
||||
if (isNewRendering()) return;
|
||||
LOG.assertTrue(!myCache.isEmpty());
|
||||
myCache.remove(myCache.size() - 1);
|
||||
}
|
||||
|
||||
private boolean isNewRendering() {
|
||||
return myEditor instanceof EditorImpl && ((EditorImpl)myEditor).myUseNewRendering;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public List<CacheEntry> getCache() {
|
||||
return myCache;
|
||||
}
|
||||
}
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* There is an ubiquitous situation when single source has various different sub-sources. E.g. there is a document
|
||||
* and its soft wraps, fold regions etc.
|
||||
* <p/>
|
||||
* So, it's often convenient to encapsulate those sources in order to provide every sub-source instance one-by-one
|
||||
* in particular order. This class does exactly that job, i.e. it encapsulates multiple {@link DataProvider} instance
|
||||
* and exposes their data by {@link DataProvider#getSortingKey() sorting order}.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 10:57:44 AM
|
||||
*/
|
||||
public class CompositeDataProvider {
|
||||
|
||||
/**
|
||||
* We use array here for performance reasons (on the basis of profiling results analysis).
|
||||
*/
|
||||
private final DataProvider<? extends Comparable<?>, ?>[] myProviders;
|
||||
|
||||
/**
|
||||
* Creates new <code>CompositeDataProvider</code> object with the given providers to register.
|
||||
*
|
||||
* @param providers providers to register within the current composite provider
|
||||
*/
|
||||
@SafeVarargs
|
||||
public CompositeDataProvider(@NotNull DataProvider<? extends Comparable<?>, ?> ... providers) {
|
||||
// We assume here that given array ownership belongs to the current object now.
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
DataProvider<? extends Comparable<?>, ?> provider = providers[i];
|
||||
if (provider.getData() == null) {
|
||||
providers[i] = null;
|
||||
}
|
||||
}
|
||||
myProviders = providers;
|
||||
sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer if there is any data at the current provider.
|
||||
* <p/>
|
||||
* I.e. it's safe to call {@link #getData()} if this method returns <code>true</code>.
|
||||
*
|
||||
* @return <code>true</code> if there is a data at the current provider; <code>false</code> otherwise
|
||||
*/
|
||||
public boolean hasData() {
|
||||
for (DataProvider<? extends Comparable<?>, ?> provider : myProviders) {
|
||||
if (provider != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks current provider to ignore all data which {@link DataProvider#getSortingKey() sorting keys} are less than the given value.
|
||||
*
|
||||
* @param sortingKey min sorting key to use
|
||||
*/
|
||||
public void advance(int sortingKey) {
|
||||
for (int i = 0; i < myProviders.length; i++) {
|
||||
DataProvider<? extends Comparable<?>, ?> provider = myProviders[i];
|
||||
if (provider == null) {
|
||||
continue;
|
||||
}
|
||||
provider.advance(sortingKey);
|
||||
if (provider.getData() == null) {
|
||||
myProviders[i] = null;
|
||||
}
|
||||
}
|
||||
sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data with the the smallest {@link DataProvider#getSortingKey() sorting key} from registered providers.
|
||||
* <p/>
|
||||
* <b>Note:</b> subsequent invocations of this method are expected to return the same value unless {@link #next()} is called.
|
||||
*
|
||||
* @param <K> data key type
|
||||
* @param <V> data value type
|
||||
* @return <code>(key; value)</code> pair that is {@link DataProvider#getData() returned from data provider} with the
|
||||
* smallest {@link DataProvider#getSortingKey() sorting key}
|
||||
* @throws IllegalStateException if no data left at the current composite provider
|
||||
* @see #hasData()
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@NotNull
|
||||
public <K, V> Pair<K, V> getData() throws IllegalStateException {
|
||||
if (!hasData()) {
|
||||
throw new IllegalStateException("No more data is available within the current provider.");
|
||||
}
|
||||
DataProvider<?, ?> provider = myProviders[0];
|
||||
Pair<K, V> result = (Pair<K, V>)provider.getData();
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(String.format("Programming error detected - registered data provider doesn't have data (%s). "
|
||||
+ "Registered providers: %s", provider, Arrays.toString(myProviders)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to ask for the {@link DataProvider#getSortingKey() sorting key} of the {@link #getData() current data}.
|
||||
* <p/>
|
||||
* It was possible to add sorting key to the {@link #getData()} result but the idea was to avoid unnecessary boxing/un-boxing.
|
||||
*
|
||||
* @return sorting key for the {@link #getData() current data} if any.
|
||||
* @throws IllegalStateException if there is no data within the current provider
|
||||
*/
|
||||
public int getSortingKey() throws IllegalStateException {
|
||||
if (!hasData()) {
|
||||
throw new IllegalStateException("No more data is available within the current provider");
|
||||
}
|
||||
|
||||
return myProviders[0].getSortingKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks current provider to drop the data {@link #getData() returned last time} and advance to the next data with the
|
||||
* smallest {@link DataProvider#getSortingKey() sorting key}.
|
||||
*
|
||||
* @return <code>true</code> if there is more data at the current provider ({@link #hasData()} returns <code>true</code>
|
||||
* and {@link #getData()} doesn't throw exception); <code>false</code> otherwise
|
||||
*/
|
||||
public boolean next() {
|
||||
if (!hasData()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataProvider<?, ?> provider = myProviders[0];
|
||||
if (!provider.next()) {
|
||||
myProviders[0] = null;
|
||||
}
|
||||
if (hasData()) {
|
||||
sort();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void sort() {
|
||||
if (myProviders.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Profiling results analysis-implied optimization.
|
||||
int i = 0;
|
||||
DataProvider<? extends Comparable<?>, ?> dataProvider = myProviders[0];
|
||||
for (int j = 1; j < myProviders.length; j++) {
|
||||
DataProvider<? extends Comparable<?>, ?> candidate = myProviders[j];
|
||||
if (candidate == null) {
|
||||
continue;
|
||||
}
|
||||
if (dataProvider == null || compare(dataProvider, candidate) > 0) {
|
||||
i = j;
|
||||
dataProvider = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
myProviders[i] = myProviders[0];
|
||||
myProviders[0] = dataProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private static int compare(DataProvider<? extends Comparable<?>, ?> o1, DataProvider<? extends Comparable<?>, ?> o2) {
|
||||
// We assume here that given data providers have data to expose.
|
||||
int result = o1.getSortingKey() - o2.getSortingKey();
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Pair<? extends Comparable<Object>, ?> d1 = (Pair<? extends Comparable<Object>, ?>)o1.getData();
|
||||
Pair<? extends Comparable<Object>, ?> d2 = (Pair<? extends Comparable<Object>, ?>)o2.getData();
|
||||
if (d1 != null && d2 != null) {
|
||||
return d1.first.compareTo(d2.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Defines contract for providing data sorted by int values.
|
||||
* <p/>
|
||||
* Expected usage is very similar to the {@link Iterator}'s one - the same {@link #getData() current data} and
|
||||
* {@link #getSortingKey() sorting key} are exposed until {@link #next()} is called.
|
||||
* <p/>
|
||||
* Implementations of this interface are not obliged to be thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 10:56:14 AM
|
||||
*/
|
||||
public interface DataProvider<K extends Comparable<? super K>, V> {
|
||||
|
||||
/**
|
||||
* @return current data if any is available; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
Pair<K, V> getData();
|
||||
|
||||
/**
|
||||
* @return sorting key for the {@link #getData() current data}
|
||||
*/
|
||||
int getSortingKey();
|
||||
|
||||
/**
|
||||
* Asks current data provider to advance to the next data if any.
|
||||
*
|
||||
* @return flag that shows if current provider has more data, i.e. {@link #getData()} is guaranteed to return
|
||||
* not-<code>null</code> value if this method returns <code>true</code>
|
||||
*/
|
||||
boolean next();
|
||||
|
||||
/**
|
||||
* Asks current provider to ignore all data which {@link DataProvider#getSortingKey() sorting keys} are less than the given value.
|
||||
*
|
||||
* @param sortingKey min sorting key to use
|
||||
*/
|
||||
void advance(int sortingKey);
|
||||
}
|
||||
-35
@@ -51,13 +51,6 @@ class EditorPosition implements Cloneable {
|
||||
myEditor = editor;
|
||||
}
|
||||
|
||||
EditorPosition(@NotNull LogicalPosition logical,
|
||||
int offset,
|
||||
@NotNull Editor editor)
|
||||
{
|
||||
this(logical, logical.toVisualPosition(), offset, editor);
|
||||
}
|
||||
|
||||
EditorPosition(@NotNull LogicalPosition logical,
|
||||
@NotNull VisualPosition visual,
|
||||
int offset,
|
||||
@@ -78,34 +71,6 @@ class EditorPosition implements Cloneable {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition buildLogicalPosition() {
|
||||
return new LogicalPosition(
|
||||
logicalLine, logicalColumn, softWrapLinesBefore, softWrapLinesCurrent, softWrapColumnDiff, foldedLines, foldingColumnDiff
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VisualPosition buildVisualPosition() {
|
||||
return new VisualPosition(visualLine, visualColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks current position to change its state assuming that it should point to the start of the next visual line.
|
||||
*/
|
||||
public void onNewLineSoftWrapAware() {
|
||||
if (myEditor.getSoftWrapModel().getSoftWrap(offset) == null) {
|
||||
onNewLine();
|
||||
return;
|
||||
}
|
||||
|
||||
softWrapLinesCurrent++;
|
||||
softWrapColumnDiff = -logicalColumn - foldingColumnDiff;
|
||||
visualLine++;
|
||||
visualColumn = 0;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
public void onNewLine() {
|
||||
softWrapLinesBefore += softWrapLinesCurrent;
|
||||
softWrapLinesCurrent = 0;
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Caches information about number of logical columns inside the collapsed single line folding.
|
||||
*/
|
||||
class FoldingData {
|
||||
private final int myWidthInColumns;
|
||||
private final FoldRegion myFoldRegion;
|
||||
|
||||
FoldingData(@NotNull FoldRegion foldRegion, int widthInColumns) {
|
||||
myFoldRegion = foldRegion;
|
||||
myWidthInColumns = widthInColumns;
|
||||
}
|
||||
|
||||
public int getCollapsedSymbolsWidthInColumns() {
|
||||
return myWidthInColumns;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FoldRegion getFoldRegion() {
|
||||
return myFoldRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "width in columns: " + myWidthInColumns + ", fold region: " + myFoldRegion;
|
||||
}
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Adapts information about fold regions received as an array to {@link DataProvider} API.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 12:42:02 PM
|
||||
*/
|
||||
public class FoldingDataProvider extends AbstractDataProvider<SoftWrapDataProviderKeys, FoldRegion> {
|
||||
|
||||
private final FoldRegion[] myFoldRegions;
|
||||
private int myIndex;
|
||||
|
||||
public FoldingDataProvider(@Nullable FoldRegion[] foldRegions) {
|
||||
super(SoftWrapDataProviderKeys.COLLAPSED_FOLDING);
|
||||
myFoldRegions = foldRegions;
|
||||
|
||||
scrollToInterested();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FoldRegion doGetData() {
|
||||
if (myFoldRegions == null || myIndex >= myFoldRegions.length) {
|
||||
return null;
|
||||
}
|
||||
return myFoldRegions[myIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
++myIndex;
|
||||
scrollToInterested();
|
||||
return doGetData() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands {@link #myIndex target index} in order to point to valid collapsed fold region if necessary.
|
||||
*/
|
||||
private void scrollToInterested() {
|
||||
if (myFoldRegions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (; myIndex < myFoldRegions.length; myIndex++) {
|
||||
FoldRegion foldRegion = myFoldRegions[myIndex];
|
||||
if (!foldRegion.isExpanded() && foldRegion.isValid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance(int sortingKey) {
|
||||
// We inline binary search here because profiling indicates that as a performance boost.
|
||||
int start = myIndex;
|
||||
int end = myFoldRegions.length - 1;
|
||||
|
||||
// We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
|
||||
while (start <= end) {
|
||||
int i = (end + start) >>> 1;
|
||||
FoldRegion foldRegion = myFoldRegions[i];
|
||||
if (foldRegion.getStartOffset() < sortingKey) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (foldRegion.getStartOffset() > sortingKey) {
|
||||
end = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
myIndex = i;
|
||||
scrollToInterested();
|
||||
return;
|
||||
}
|
||||
myIndex = start;
|
||||
scrollToInterested();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSortingKey() {
|
||||
if (myFoldRegions == null || myIndex >= myFoldRegions.length) {
|
||||
return 0;
|
||||
}
|
||||
return myFoldRegions[myIndex].getStartOffset();
|
||||
}
|
||||
}
|
||||
+15
-45
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SoftWrap;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
@@ -30,8 +32,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
* @since 11/17/10 9:33 AM
|
||||
*/
|
||||
public class IncrementalCacheUpdateEvent {
|
||||
private static Logger LOG = Logger.getInstance(IncrementalCacheUpdateEvent.class);
|
||||
|
||||
private final int myStartOffset;
|
||||
private final int myMandatoryEndOffset;
|
||||
private int myActualEndOffset = -1;
|
||||
@@ -54,16 +54,16 @@ public class IncrementalCacheUpdateEvent {
|
||||
*
|
||||
* @param event object that describes document change that caused cache update
|
||||
*/
|
||||
IncrementalCacheUpdateEvent(@NotNull DocumentEvent event, @NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) {
|
||||
this(event.getOffset(), event.getOffset() + event.getOldLength(), event.getOffset() + event.getNewLength(), mapper, editor);
|
||||
IncrementalCacheUpdateEvent(@NotNull DocumentEvent event, @NotNull EditorImpl editor) {
|
||||
this(event.getOffset(), event.getOffset() + event.getOldLength(), event.getOffset() + event.getNewLength(), editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new <code>IncrementalCacheUpdateEvent</code> object for the event not changing document length
|
||||
* (like expansion of folded region).
|
||||
*/
|
||||
IncrementalCacheUpdateEvent(int startOffset, int endOffset, @NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) {
|
||||
this(startOffset, endOffset, endOffset, mapper, editor);
|
||||
IncrementalCacheUpdateEvent(int startOffset, int endOffset, @NotNull EditorImpl editor) {
|
||||
this(startOffset, endOffset, endOffset, editor);
|
||||
myNewEndLogicalLine = myOldEndLogicalLine;
|
||||
}
|
||||
|
||||
@@ -82,23 +82,14 @@ public class IncrementalCacheUpdateEvent {
|
||||
myOldEndLogicalLine = myNewEndLogicalLine = Math.max(0, document.getLineCount() - 1);
|
||||
}
|
||||
|
||||
private IncrementalCacheUpdateEvent(int startOffset, int oldEndOffset, int newEndOffset,
|
||||
@NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) {
|
||||
if (editor.myUseNewRendering) {
|
||||
VisualLineInfo info = getVisualLineInfo(editor, startOffset, false);
|
||||
if (info.startsWithSoftWrap) {
|
||||
info = getVisualLineInfo(editor, info.startOffset, true);
|
||||
}
|
||||
myStartOffset = info.startOffset;
|
||||
myStartLogicalPosition = editor.offsetToLogicalPosition(myStartOffset);
|
||||
myStartVisualPosition = new VisualPosition(info.visualLine, 0);
|
||||
}
|
||||
else {
|
||||
myStartOffset = mapper.getPreviousVisualLineStartOffset(startOffset);
|
||||
myStartLogicalPosition = mapper.offsetToLogicalPosition(myStartOffset);
|
||||
LOG.assertTrue(myStartLogicalPosition.visualPositionAware);
|
||||
myStartVisualPosition = myStartLogicalPosition.toVisualPosition();
|
||||
private IncrementalCacheUpdateEvent(int startOffset, int oldEndOffset, int newEndOffset, @NotNull EditorImpl editor) {
|
||||
VisualLineInfo info = getVisualLineInfo(editor, startOffset, false);
|
||||
if (info.startsWithSoftWrap) {
|
||||
info = getVisualLineInfo(editor, info.startOffset, true);
|
||||
}
|
||||
myStartOffset = info.startOffset;
|
||||
myStartLogicalPosition = editor.offsetToLogicalPosition(myStartOffset);
|
||||
myStartVisualPosition = new VisualPosition(info.visualLine, 0);
|
||||
myMandatoryEndOffset = newEndOffset;
|
||||
myLengthDiff = newEndOffset - oldEndOffset;
|
||||
myOldEndLogicalLine = editor.getDocument().getLineNumber(oldEndOffset);
|
||||
@@ -190,27 +181,6 @@ public class IncrementalCacheUpdateEvent {
|
||||
return myLengthDiff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns change in document line count for the event causing soft wrap recalculation.
|
||||
*/
|
||||
public int getLogicalLinesDiff() {
|
||||
return myNewEndLogicalLine - myOldEndLogicalLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns line number for initial change's starting point
|
||||
*/
|
||||
public int getStartLogicalLine() {
|
||||
return myStartLogicalPosition.line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns line number for initial change's ending point
|
||||
*/
|
||||
public int getOldEndLogicalLine() {
|
||||
return myOldEndLogicalLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "startOffset=" + myStartOffset +
|
||||
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SoftWrap;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class LogicalToOffsetCalculationStrategy extends AbstractMappingStrategy<Integer> {
|
||||
|
||||
private LogicalPosition targetPosition;
|
||||
|
||||
LogicalToOffsetCalculationStrategy(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull List<CacheEntry> cache) {
|
||||
super(editor, storage, cache);
|
||||
}
|
||||
|
||||
void init(LogicalPosition logical) {
|
||||
CacheEntry entry = MappingUtil.getCacheEntryForLogicalPosition(logical, myCache);
|
||||
if (entry == null) {
|
||||
if (logical.line >= myEditor.getDocument().getLineCount()) {
|
||||
setEagerMatch(myEditor.getDocument().getTextLength());
|
||||
}
|
||||
else {
|
||||
setEagerMatch(Math.min(myEditor.getDocument().getLineStartOffset(logical.line) + logical.column,
|
||||
myEditor.getDocument().getLineEndOffset(logical.line)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (entry.endLogicalLine == logical.line && entry.endLogicalColumn <= logical.column) {
|
||||
setEagerMatch(entry.endOffset);
|
||||
return;
|
||||
}
|
||||
reset();
|
||||
targetPosition = logical;
|
||||
setTargetEntry(entry, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Integer buildIfExceeds(EditorPosition position, int offset) {
|
||||
if (position.logicalLine != targetPosition.line) {
|
||||
return null;
|
||||
}
|
||||
int result = position.offset + targetPosition.column - position.logicalColumn;
|
||||
return result > offset ? null : result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer processFoldRegion(@NotNull EditorPosition position, @NotNull FoldRegion foldRegion) {
|
||||
int startLine = position.logicalLine;
|
||||
int startColumn = position.logicalColumn;
|
||||
int startX = position.x;
|
||||
|
||||
advancePositionOnFolding(position, foldRegion);
|
||||
|
||||
if (position.logicalLine < targetPosition.line
|
||||
|| position.logicalLine == targetPosition.line && position.logicalColumn <= targetPosition.column) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Document document = myEditor.getDocument();
|
||||
int lineEndOffset = document.getLineEndOffset(targetPosition.line);
|
||||
int result = SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor)
|
||||
.calcSoftWrapUnawareOffset(targetPosition.line == startLine ? foldRegion.getStartOffset()
|
||||
: document.getLineStartOffset(targetPosition.line),
|
||||
lineEndOffset,
|
||||
targetPosition.line == startLine ? startColumn : 0,
|
||||
targetPosition.column,
|
||||
targetPosition.line == startLine ? startX : 0);
|
||||
return result < 0 ? lineEndOffset : result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Integer buildIfExceeds(@NotNull EditorPosition position, @NotNull FoldRegion foldRegion) {
|
||||
throw new RuntimeException("Unexpected invocation");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Integer buildIfExceeds(EditorPosition position, TabData tabData) {
|
||||
if (position.logicalLine != targetPosition.line) {
|
||||
return null;
|
||||
}
|
||||
return position.logicalColumn + tabData.widthInColumns > targetPosition.column ? position.offset : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer processSoftWrap(@NotNull EditorPosition position, SoftWrap softWrap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Integer build(@NotNull EditorPosition position) {
|
||||
return position.offset + targetPosition.column - position.logicalColumn;
|
||||
}
|
||||
}
|
||||
-182
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 9, 2010 10:08:08 AM
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
class LogicalToVisualMappingStrategy extends AbstractMappingStrategy<VisualPosition> {
|
||||
|
||||
private LogicalPosition myTargetLogical;
|
||||
|
||||
LogicalToVisualMappingStrategy(@NotNull LogicalPosition logical, @NotNull EditorEx editor, @NotNull SoftWrapsStorage storage,
|
||||
@NotNull List<CacheEntry> cache)
|
||||
throws IllegalStateException
|
||||
{
|
||||
super(editor, storage, cache);
|
||||
myTargetLogical = logical;
|
||||
}
|
||||
|
||||
public void init(@NotNull final LogicalPosition logical, final @NotNull List<CacheEntry> cache) {
|
||||
reset();
|
||||
|
||||
myTargetLogical = logical;
|
||||
int start = 0;
|
||||
int end = cache.size() - 1;
|
||||
|
||||
// We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
|
||||
while (start <= end) {
|
||||
int i = (end + start) >>> 1;
|
||||
CacheEntry cacheEntry = cache.get(i);
|
||||
|
||||
// There is a possible case that single logical line is represented on multiple visual lines due to soft wraps processing.
|
||||
// Hence, we check for bot logical line and logical columns during searching 'anchor' cache entry.
|
||||
|
||||
if (cacheEntry.endLogicalLine < logical.line
|
||||
|| (cacheEntry.endLogicalLine == logical.line && myStorage.getSoftWrap(cacheEntry.endOffset) != null
|
||||
&& cacheEntry.endLogicalColumn <= logical.column)) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (cacheEntry.startLogicalLine > logical.line
|
||||
|| (cacheEntry.startLogicalLine == logical.line
|
||||
&& cacheEntry.startLogicalColumn > logical.column)) {
|
||||
end = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// There is a possible case that currently found cache entry corresponds to soft-wrapped line and soft wrap occurred
|
||||
// at target logical column. We need to return cache entry for the next visual line then (because single logical column
|
||||
// is shared for 'before soft wrap' and 'after soft wrap' positions and we want to use the one that points to
|
||||
// 'after soft wrap' position).
|
||||
if (cacheEntry.endLogicalLine == logical.line && cacheEntry.endLogicalColumn == logical.column && i < cache.size() - 1) {
|
||||
CacheEntry nextLineCacheEntry = cache.get(i + 1);
|
||||
if (nextLineCacheEntry.startLogicalLine == logical.line
|
||||
&& nextLineCacheEntry.startLogicalColumn == logical.column) {
|
||||
setInitialPosition(nextLineCacheEntry.buildStartLinePosition());
|
||||
return;
|
||||
}
|
||||
}
|
||||
setInitialPosition(cacheEntry.buildStartLinePosition());
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Can't map logical position (%s) to visual position. Reason: no cached information information about target visual "
|
||||
+ "line is found. Registered entries: %s", logical, cache
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VisualPosition buildIfExceeds(EditorPosition position, int offset) {
|
||||
if (position.logicalLine < myTargetLogical.line) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int diff = myTargetLogical.column - position.logicalColumn;
|
||||
if (offset - position.offset < diff) {
|
||||
return null;
|
||||
}
|
||||
|
||||
position.visualColumn += diff;
|
||||
// Don't update other dimensions like logical position and offset because we need only visual position here.
|
||||
return position.buildVisualPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VisualPosition buildIfExceeds(@NotNull EditorPosition context, @NotNull FoldRegion foldRegion) {
|
||||
int foldEndLine = myEditor.getDocument().getLineNumber(foldRegion.getEndOffset());
|
||||
if (myTargetLogical.line > foldEndLine) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (myTargetLogical.line < foldEndLine) {
|
||||
// Map all logical position that point inside collapsed fold region to visual position of its start.
|
||||
return context.buildVisualPosition();
|
||||
}
|
||||
|
||||
FoldingData data = getFoldRegionData(foldRegion);
|
||||
int foldEndColumn;
|
||||
if (data == null) {
|
||||
int xStart = myEditor.getDocument().getLineNumber(foldRegion.getStartOffset()) == foldEndLine ? context.x : 0;
|
||||
foldEndColumn = SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor).toVisualColumnSymbolsNumber(
|
||||
foldRegion.getStartOffset(), foldRegion.getEndOffset(), xStart
|
||||
);
|
||||
} else {
|
||||
foldEndColumn = data.getCollapsedSymbolsWidthInColumns();
|
||||
}
|
||||
|
||||
if (foldEndLine == context.logicalLine) {
|
||||
// Single-line fold region.
|
||||
foldEndColumn += context.logicalColumn;
|
||||
}
|
||||
|
||||
if (foldEndColumn <= myTargetLogical.column) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map all logical position that point inside collapsed fold region to visual position of its start.
|
||||
return context.buildVisualPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VisualPosition buildIfExceeds(EditorPosition context, TabData tabData) {
|
||||
if (context.logicalLine < myTargetLogical.line) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int diff = myTargetLogical.column - context.logicalColumn;
|
||||
if (diff >= tabData.widthInColumns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
context.logicalColumn += diff;
|
||||
context.visualColumn += diff;
|
||||
|
||||
return context.buildVisualPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisualPosition processSoftWrap(@NotNull EditorPosition position, SoftWrap softWrap) {
|
||||
position.visualColumn = softWrap.getIndentInColumns();
|
||||
position.softWrapColumnDiff += softWrap.getIndentInColumns();
|
||||
|
||||
if (position.logicalLine < myTargetLogical.line || position.logicalColumn != myTargetLogical.column) {
|
||||
return null;
|
||||
}
|
||||
return position.buildVisualPosition();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public VisualPosition build(@NotNull EditorPosition position) {
|
||||
int diff = myTargetLogical.column - position.logicalColumn;
|
||||
position.logicalColumn += diff;
|
||||
position.visualColumn += diff;
|
||||
position.offset += diff;
|
||||
return position.buildVisualPosition();
|
||||
}
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import com.intellij.openapi.editor.SoftWrap;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Defines contract for the strategy that knows how to map one document dimension to another (e.g. visual position to logical position).
|
||||
*
|
||||
* @param <T> target dimension type
|
||||
*/
|
||||
interface MappingStrategy<T> {
|
||||
|
||||
/**
|
||||
* @return target mapped dimension if it's possible to perform the mapping eagerly; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
T eagerMatch();
|
||||
|
||||
/**
|
||||
* Builds initial position to start calculation from.
|
||||
* <p/>
|
||||
* It's assumed that we store information about 'anchor' document positions like visual line starts and calculate
|
||||
* target result starting from the nearest position.
|
||||
*
|
||||
* @return initial position to use for target result calculation
|
||||
*/
|
||||
@NotNull
|
||||
EditorPosition buildInitialPosition();
|
||||
|
||||
/**
|
||||
* Notifies current strategy that there are no special symbols and regions between the document position identified
|
||||
* by the current state of the given position and given offset. I.e. it's safe to assume that all symbols between the offset
|
||||
* identified by the given position and given offset occupy one visual and logical column.
|
||||
*
|
||||
* @param position currently processed position
|
||||
* @param offset nearest offset to the one identified by the given position that conforms to requirement that every symbol
|
||||
* between them increments offset, visual and logical position by one
|
||||
* @return resulting dimension if it's located between the given position
|
||||
* and given offset if any; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
T advance(@NotNull EditorPosition position, int offset);
|
||||
|
||||
/**
|
||||
* Notifies current strategy that soft wrap is encountered during the processing. There are two ways to continue the processing then:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>Target dimension is located after the given soft wrap;</li>
|
||||
* <li>Target dimension is located within the given soft wrap bounds;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* @param position current processing position
|
||||
* @param softWrap soft wrap encountered during the processing
|
||||
* @return target document dimension if it's located within the bounds of the given soft wrap; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
T processSoftWrap(@NotNull EditorPosition position, SoftWrap softWrap);
|
||||
|
||||
/**
|
||||
* Notifies current strategy that collapsed fold region is encountered during the processing. There are two ways to
|
||||
* continue the processing then:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>Target dimension is located after the given fold region;</li>
|
||||
* <li>Target dimension is located within the given fold region bounds;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* @param position current processing position
|
||||
* @param foldRegion collapsed fold region encountered during the processing
|
||||
* @return target document dimension if it's located within the bounds of the given fold region;
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
T processFoldRegion(@NotNull EditorPosition position, @NotNull FoldRegion foldRegion);
|
||||
|
||||
/**
|
||||
* Notifies current strategy that tabulation symbols is encountered during the processing. Tabulation symbols
|
||||
* have special treatment because they may occupy different number of visual and logical columns.
|
||||
* See {@link EditorUtil#nextTabStop(int, Editor)} for more details. So, there are two ways to continue the processing then:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>Target dimension is located after the given tabulation symbol bounds;</li>
|
||||
* <li>Target dimension is located within the given tabulation symbol bounds;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* @param position current processing position
|
||||
* @param tabData document position for the active tabulation symbol encountered during the processing
|
||||
* @return target document dimension if it's located within the bounds of the given fold region;
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
T processTabulation(@NotNull EditorPosition position, TabData tabData);
|
||||
|
||||
/**
|
||||
* This method is assumed to be called when there are no special symbols between the document position identified by the
|
||||
* given position and target dimension. E.g. this method may be called when we perform {@code visual -> logical} mapping
|
||||
* and there are no soft wraps, collapsed fold regions and tabulation symbols on a current visual line.
|
||||
*
|
||||
* @param position current processing position that identifies active document position
|
||||
* @return resulting dimension that is built on the basis of the given position and target anchor dimension
|
||||
*/
|
||||
@NotNull
|
||||
T build(@NotNull EditorPosition position);
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds utility methods for document dimensions mapping.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 9, 2010 9:38:42 AM
|
||||
*/
|
||||
public class MappingUtil {
|
||||
|
||||
private MappingUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to map given offset to the index of target {@link CacheEntry} that holds information about visual line with that offset.
|
||||
*
|
||||
* @param offset target offset
|
||||
* @param document document which dimensions are being mapped
|
||||
* @param cache parsed cache entries list that hold information about given document positions and are sorted by visual lines
|
||||
* in ascending order
|
||||
* @return non-negative index of target {@link CacheEntry} that holds information about visual line with given offset
|
||||
* if possible; negative value that indicates insertion point of cache entry for the given offset is to be located.
|
||||
* That value follows {@link Collections#binarySearch(List, Object)} contract, i.e. real index is calculated
|
||||
* by <code>'-returned_index - 1'</code>
|
||||
*/
|
||||
public static int getCacheEntryIndexForOffset(int offset, Document document, List<CacheEntry> cache) {
|
||||
if (offset >= document.getTextLength() && (cache.isEmpty() || cache.get(cache.size() - 1).endOffset < offset)) {
|
||||
return -(cache.size() + 1);
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = cache.size() - 1;
|
||||
|
||||
// We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
|
||||
while (start <= end) {
|
||||
int i = (end + start) >>> 1;
|
||||
CacheEntry cacheEntry = cache.get(i);
|
||||
if (cacheEntry.endOffset < offset) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (cacheEntry.startOffset > offset) {
|
||||
end = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// There is a possible case that currently found cache entry corresponds to soft-wrapped line and soft wrap occurred
|
||||
// at target offset. We need to return cache entry for the next visual line then (because document offset shared with
|
||||
// soft wrap offset is assumed to point to 'after soft wrap' position).
|
||||
if (offset == cacheEntry.endOffset && i < cache.size() - 1) {
|
||||
CacheEntry nextLineCacheEntry = cache.get(i + 1);
|
||||
if (nextLineCacheEntry.startOffset == offset) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
return -(start + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CacheEntry getCacheEntryForLogicalPosition(@NotNull LogicalPosition position, @NotNull List<CacheEntry> cache) {
|
||||
int start = 0;
|
||||
int end = cache.size() - 1;
|
||||
|
||||
while (start <= end) {
|
||||
int i = (end + start) >>> 1;
|
||||
CacheEntry cacheEntry = cache.get(i);
|
||||
if (cacheEntry.startLogicalLine < position.line
|
||||
|| cacheEntry.startLogicalLine == position.line && cacheEntry.startLogicalColumn < position.column) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (cacheEntry.startLogicalLine > position.line
|
||||
|| cacheEntry.startLogicalLine == position.line && cacheEntry.startLogicalColumn > position.column) {
|
||||
end = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return assertEnd(position, cache.get(i));
|
||||
}
|
||||
return end < 0 ? null : assertEnd(position, cache.get(end));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CacheEntry assertEnd(@NotNull LogicalPosition position, @NotNull CacheEntry entry) {
|
||||
return position.line <= entry.endLogicalLine ? entry : null;
|
||||
}
|
||||
}
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 9, 2010 9:20:55 AM
|
||||
*/
|
||||
class OffsetToLogicalCalculationStrategy extends AbstractMappingStrategy<LogicalPosition> {
|
||||
|
||||
private int myTargetOffset;
|
||||
|
||||
OffsetToLogicalCalculationStrategy(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull List<CacheEntry> cache)
|
||||
{
|
||||
super(editor, storage, cache);
|
||||
}
|
||||
|
||||
public void init(final int targetOffset, final List<CacheEntry> cache) {
|
||||
reset();
|
||||
|
||||
myTargetOffset = targetOffset;
|
||||
Document document = myEditor.getDocument();
|
||||
if (targetOffset == 0) {
|
||||
LogicalPosition eager = new LogicalPosition(0, 0, 0, 0, 0, 0, 0);
|
||||
setEagerMatch(eager);
|
||||
return;
|
||||
}
|
||||
else if (targetOffset >= document.getTextLength()) {
|
||||
if (cache.isEmpty()) {
|
||||
setFirstInitialPosition();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// We expect the following possible cases here:
|
||||
// 1. There is a cache entry for the target line;
|
||||
// 1.1. Document ends by line feed;
|
||||
// 1.2. Document ends by the symbol that is not line feed;
|
||||
// 2. There is no cache entry for the target line;;
|
||||
|
||||
CacheEntry lastEntry = cache.get(cache.size() - 1);
|
||||
if (lastEntry.endOffset >= targetOffset - 1) {
|
||||
EditorPosition position = lastEntry.buildEndLinePosition();
|
||||
if (document.getCharsSequence().charAt(document.getTextLength() - 1) == '\n') {
|
||||
position.onNewLineSoftWrapAware();
|
||||
}
|
||||
setEagerMatch(position.buildLogicalPosition());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (cache.size() > 0 && cache.get(cache.size() - 1).endOffset < targetOffset) {
|
||||
EditorPosition position = cache.get(cache.size() - 1).buildEndLinePosition();
|
||||
position.onNewLineSoftWrapAware();
|
||||
setInitialPosition(position);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = MappingUtil.getCacheEntryIndexForOffset(targetOffset, myEditor.getDocument(), cache);
|
||||
CacheEntry cacheEntry = null;
|
||||
if (i >= 0) {
|
||||
CacheEntry candidate = cache.get(i);
|
||||
if (candidate.startOffset <= targetOffset) {
|
||||
cacheEntry = candidate;
|
||||
}
|
||||
}
|
||||
else if (i < -1) {
|
||||
i = -i - 2;
|
||||
if (i < myCache.size()) {
|
||||
cacheEntry = myCache.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheEntry == null) {
|
||||
setFirstInitialPosition();
|
||||
}
|
||||
else if (cacheEntry.startOffset <= targetOffset && cacheEntry.endOffset >= targetOffset) {
|
||||
setTargetEntry(cacheEntry, true);
|
||||
}
|
||||
else {
|
||||
setInitialPosition(cacheEntry.buildStartLinePosition());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LogicalPosition buildIfExceeds(EditorPosition position, int offset) {
|
||||
if (myTargetOffset >= offset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Document document = myEditor.getDocument();
|
||||
int logicalLine = document.getLineNumber(myTargetOffset);
|
||||
int linesDiff = logicalLine - position.logicalLine;
|
||||
if (linesDiff > 0) {
|
||||
position.onNewLine();
|
||||
int column = myTargetOffset - document.getLineStartOffset(logicalLine);
|
||||
position.visualColumn = column;
|
||||
position.logicalColumn = column;
|
||||
}
|
||||
else {
|
||||
int columnsDiff = myTargetOffset - position.offset;
|
||||
position.logicalColumn += columnsDiff;
|
||||
position.visualColumn += columnsDiff;
|
||||
}
|
||||
position.logicalLine = logicalLine;
|
||||
position.offset = myTargetOffset;
|
||||
|
||||
return position.buildLogicalPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LogicalPosition buildIfExceeds(@NotNull EditorPosition position, @NotNull FoldRegion foldRegion) {
|
||||
if (myTargetOffset >= foldRegion.getEndOffset()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Document document = myEditor.getDocument();
|
||||
int targetLogicalLine = document.getLineNumber(myTargetOffset);
|
||||
if (targetLogicalLine == position.logicalLine) {
|
||||
// Target offset is located on the same logical line as folding start.
|
||||
position.logicalColumn += SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor).toVisualColumnSymbolsNumber(
|
||||
foldRegion.getStartOffset(), myTargetOffset, position.x
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Target offset is located on a different line with folding start.
|
||||
position.logicalColumn = SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor).toVisualColumnSymbolsNumber(
|
||||
foldRegion.getStartOffset(), myTargetOffset, 0
|
||||
);
|
||||
position.softWrapColumnDiff = 0;
|
||||
int linesDiff = document.getLineNumber(myTargetOffset) - document.getLineNumber(foldRegion.getStartOffset());
|
||||
position.logicalLine += linesDiff;
|
||||
position.foldedLines += linesDiff;
|
||||
position.softWrapLinesBefore += position.softWrapLinesCurrent;
|
||||
position.softWrapLinesCurrent = 0;
|
||||
}
|
||||
|
||||
position.foldingColumnDiff = position.visualColumn - position.softWrapColumnDiff - position.logicalColumn;
|
||||
position.offset = myTargetOffset;
|
||||
return position.buildLogicalPosition();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected LogicalPosition buildIfExceeds(EditorPosition context, TabData tabData) {
|
||||
if (tabData.offset == myTargetOffset) {
|
||||
return context.buildLogicalPosition();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LogicalPosition processSoftWrap(@NotNull EditorPosition position, SoftWrap softWrap) {
|
||||
position.visualColumn = softWrap.getIndentInColumns();
|
||||
position.softWrapColumnDiff += softWrap.getIndentInColumns();
|
||||
if (softWrap.getStart() == myTargetOffset) {
|
||||
return position.buildLogicalPosition();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition build(@NotNull EditorPosition position) {
|
||||
Document document = myEditor.getDocument();
|
||||
int logicalLine = document.getLineNumber(myTargetOffset);
|
||||
int linesDiff = logicalLine - position.logicalLine;
|
||||
if (linesDiff > 0) {
|
||||
position.onNewLine();
|
||||
position.logicalLine = logicalLine;
|
||||
int column = myTargetOffset - document.getLineStartOffset(logicalLine);
|
||||
position.logicalColumn = column;
|
||||
position.visualColumn = column;
|
||||
}
|
||||
else {
|
||||
int columnsDiff = myTargetOffset - position.offset;
|
||||
position.logicalColumn += columnsDiff;
|
||||
position.visualColumn += columnsDiff;
|
||||
}
|
||||
position.offset = myTargetOffset;
|
||||
return position.buildLogicalPosition();
|
||||
}
|
||||
}
|
||||
+10
-101
@@ -182,7 +182,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
int lastOffset = lastRecalculatedOffset[0];
|
||||
if (range.getEndOffset() > lastOffset) {
|
||||
recalculateSoftWraps(new IncrementalCacheUpdateEvent(Math.max(range.getStartOffset(), lastOffset), range.getEndOffset(),
|
||||
myDataMapper, myEditor));
|
||||
myEditor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,8 +259,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
myContext.fontType = attributes.getFontType();
|
||||
myContext.rangeEndOffset = event.getMandatoryEndOffset();
|
||||
|
||||
EditorPosition position = myEditor.myUseNewRendering ? new EditorPosition(logical, event.getStartVisualPosition(), start, myEditor) :
|
||||
new EditorPosition(logical, start, myEditor);
|
||||
EditorPosition position = new EditorPosition(logical, event.getStartVisualPosition(), start, myEditor);
|
||||
position.x = start == 0 ? myEditor.getPrefixTextWidthInPixels() : 0;
|
||||
int spaceWidth = EditorUtil.getSpaceWidth(myContext.fontType, myEditor);
|
||||
int plainSpaceWidth = EditorUtil.getSpaceWidth(Font.PLAIN, myEditor);
|
||||
@@ -280,8 +279,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
myContext.lineStartPosition.visualColumn = 0;
|
||||
myContext.lineStartPosition.softWrapColumnDiff -= softWrapAtStartPosition.getIndentInColumns();
|
||||
myContext.softWrapStartOffset++;
|
||||
|
||||
notifyListenersOnVisualLineStart(myContext.lineStartPosition);
|
||||
}
|
||||
|
||||
myContext.inlays = myEditor.getInlayModel().getInlineElementsInRange(start, endOffsetUpperEstimate);
|
||||
@@ -316,7 +313,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
if (myContext.delayedSoftWrap != null) {
|
||||
myStorage.remove(myContext.delayedSoftWrap);
|
||||
}
|
||||
notifyListenersOnVisualLineEnd();
|
||||
event.setActualEndOffset(myContext.currentPosition.offset);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Soft wrap recalculation done: " + event.toString() + ". " + (event.getActualEndOffset() - event.getStartOffset()) + " characters processed");
|
||||
@@ -358,7 +354,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
int placeholderWidthInPixels = 0;
|
||||
for (int i = 0; i < placeholder.length(); i++) {
|
||||
char c = placeholder.charAt(i);
|
||||
if (myEditor.myUseNewRendering && c == '\n') c = ' '; // we display \n as space (see com.intellij.openapi.editor.impl.view.EditorView.getFoldRegionLayout)
|
||||
if (c == '\n') c = ' '; // we display \n as space (see com.intellij.openapi.editor.impl.view.EditorView.getFoldRegionLayout)
|
||||
placeholderWidthInPixels += SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor)
|
||||
.charWidth(c, myContext.fontType);
|
||||
}
|
||||
@@ -366,8 +362,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
if (myContext.delayedSoftWrap == null) {
|
||||
int newX = myContext.currentPosition.x + placeholderWidthInPixels;
|
||||
|
||||
notifyListenersOnVisualLineStart(myContext.lineStartPosition);
|
||||
|
||||
if (!myContext.exceedsVisualEdge(newX) || myContext.currentPosition.offset == myContext.lineStartPosition.offset) {
|
||||
myContext.advance(foldRegion, placeholderWidthInPixels);
|
||||
return false;
|
||||
@@ -396,7 +390,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
}
|
||||
myContext.softWrapStartOffset = softWrap.getStart();
|
||||
if (softWrap.getStart() < myContext.tokenStartOffset) {
|
||||
revertListeners(softWrap.getStart(), myContext.currentPosition.visualLine);
|
||||
for (int j = foldRegion.getStartOffset() - 1; j >= softWrap.getStart(); j--) {
|
||||
int pixelsDiff = myOffset2widthInPixels.data[j - myOffset2widthInPixels.anchor];
|
||||
int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getPlainSpaceWidth());
|
||||
@@ -405,14 +398,12 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
myContext.currentPosition.visualColumn -= columnsDiff;
|
||||
}
|
||||
}
|
||||
notifyListenersOnSoftWrapLineFeed(true);
|
||||
|
||||
myContext.currentPosition.visualColumn = 0;
|
||||
myContext.currentPosition.softWrapColumnDiff = myContext.currentPosition.visualColumn - myContext.currentPosition.foldingColumnDiff
|
||||
- myContext.currentPosition.logicalColumn;
|
||||
myContext.currentPosition.softWrapLinesCurrent++;
|
||||
myContext.currentPosition.visualLine++;
|
||||
notifyListenersOnSoftWrapLineFeed(false);
|
||||
|
||||
myContext.currentPosition.x = softWrap.getIndentInPixels();
|
||||
myContext.currentPosition.visualColumn = softWrap.getIndentInColumns();
|
||||
@@ -514,12 +505,8 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
private boolean checkIsDoneAfterSoftWrap() {
|
||||
SoftWrapImpl lastSoftWrap = myDataMapper.getLastSoftWrap();
|
||||
LOG.assertTrue(lastSoftWrap != null);
|
||||
if (myContext.currentPosition.offset > myContext.rangeEndOffset
|
||||
&& myDataMapper.matchesOldSoftWrap(lastSoftWrap, myEventBeingProcessed.getLengthDiff())) {
|
||||
myDataMapper.removeLastCacheEntry();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return myContext.currentPosition.offset > myContext.rangeEndOffset
|
||||
&& myDataMapper.matchesOldSoftWrap(lastSoftWrap, myEventBeingProcessed.getLengthDiff());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,7 +558,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
}
|
||||
|
||||
if (wrapPosition != null){
|
||||
revertListeners(wrapPosition.offset, wrapPosition.visualLine);
|
||||
myContext.currentPosition = wrapPosition;
|
||||
softWrap = registerSoftWrap(wrapPosition.offset, myContext.getSpaceWidth(), myContext.logicalLineData);
|
||||
myContext.tokenStartOffset = wrapPosition.offset;
|
||||
@@ -584,7 +570,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
|
||||
myContext.skipToLineEnd = false;
|
||||
|
||||
notifyListenersOnVisualLineStart(myContext.lineStartPosition);
|
||||
int actualSoftWrapOffset = softWrap.getStart();
|
||||
|
||||
// There are three possible options:
|
||||
@@ -600,7 +585,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
}
|
||||
else if (actualSoftWrapOffset < offset) {
|
||||
if (revertedToFoldRegion == null) {
|
||||
revertListeners(actualSoftWrapOffset, myContext.currentPosition.visualLine);
|
||||
for (int j = offset - 1; j >= actualSoftWrapOffset; j--) {
|
||||
int pixelsDiff = myOffset2widthInPixels.data[j - myOffset2widthInPixels.anchor];
|
||||
int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getPlainSpaceWidth());
|
||||
@@ -779,14 +763,11 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
}
|
||||
|
||||
private void processSoftWrap(SoftWrap softWrap) {
|
||||
notifyListenersOnSoftWrapLineFeed(true);
|
||||
|
||||
EditorPosition position = myContext.currentPosition;
|
||||
position.visualColumn = 0;
|
||||
position.softWrapColumnDiff = position.visualColumn - position.foldingColumnDiff - position.logicalColumn;
|
||||
position.softWrapLinesCurrent++;
|
||||
position.visualLine++;
|
||||
notifyListenersOnSoftWrapLineFeed(false);
|
||||
myContext.lineStartPosition.from(myContext.currentPosition);
|
||||
|
||||
position.x = softWrap.getIndentInPixels();
|
||||
@@ -893,8 +874,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
|
||||
private void updateLastTopLeftCornerOffset() {
|
||||
int visualLine = 1 + myEditor.getScrollingModel().getVisibleArea().y / myEditor.getLineHeight();
|
||||
myLastTopLeftCornerOffset = myEditor.myUseNewRendering ? myEditor.visualLineStartOffset(visualLine) :
|
||||
myDataMapper.getVisualLineStartOffset(visualLine);
|
||||
myLastTopLeftCornerOffset = myEditor.visualLineStartOffset(visualLine);
|
||||
}
|
||||
|
||||
private int getNumberOfSoftWrapsBefore(int offset) {
|
||||
@@ -920,64 +900,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
return myListeners.remove(listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void revertListeners(int offset, int visualLine) {
|
||||
for (int i = 0; i < myListeners.size(); i++) {
|
||||
// Avoid unnecessary Iterator object construction as this method is expected to be called frequently.
|
||||
SoftWrapAwareDocumentParsingListener listener = myListeners.get(i);
|
||||
listener.revertToOffset(offset, visualLine);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void notifyListenersOnFoldRegion(@NotNull FoldRegion foldRegion, int collapsedFoldingWidthInColumns, int visualLine) {
|
||||
for (int i = 0; i < myListeners.size(); i++) {
|
||||
// Avoid unnecessary Iterator object construction as this method is expected to be called frequently.
|
||||
SoftWrapAwareDocumentParsingListener listener = myListeners.get(i);
|
||||
listener.onCollapsedFoldRegion(foldRegion, collapsedFoldingWidthInColumns, visualLine);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void notifyListenersOnVisualLineStart(@NotNull EditorPosition position) {
|
||||
for (int i = 0; i < myListeners.size(); i++) {
|
||||
// Avoid unnecessary Iterator object construction as this method is expected to be called frequently.
|
||||
SoftWrapAwareDocumentParsingListener listener = myListeners.get(i);
|
||||
listener.onVisualLineStart(position);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void notifyListenersOnVisualLineEnd() {
|
||||
for (int i = 0; i < myListeners.size(); i++) {
|
||||
// Avoid unnecessary Iterator object construction as this method is expected to be called frequently.
|
||||
SoftWrapAwareDocumentParsingListener listener = myListeners.get(i);
|
||||
listener.onVisualLineEnd(myContext.currentPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void notifyListenersOnTabulation(int widthInColumns) {
|
||||
for (int i = 0; i < myListeners.size(); i++) {
|
||||
// Avoid unnecessary Iterator object construction as this method is expected to be called frequently.
|
||||
SoftWrapAwareDocumentParsingListener listener = myListeners.get(i);
|
||||
listener.onTabulation(myContext.currentPosition, widthInColumns);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void notifyListenersOnSoftWrapLineFeed(boolean before) {
|
||||
for (int i = 0; i < myListeners.size(); i++) {
|
||||
// Avoid unnecessary Iterator object construction as this method is expected to be called frequently.
|
||||
SoftWrapAwareDocumentParsingListener listener = myListeners.get(i);
|
||||
if (before) {
|
||||
listener.beforeSoftWrapLineFeed(myContext.currentPosition);
|
||||
}
|
||||
else {
|
||||
listener.afterSoftWrapLineFeed(myContext.currentPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"})
|
||||
private void notifyListenersOnCacheUpdateStart(IncrementalCacheUpdateEvent event) {
|
||||
@@ -998,7 +920,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
}
|
||||
|
||||
public void beforeDocumentChange(DocumentEvent event) {
|
||||
myDocumentChangedEvent = new IncrementalCacheUpdateEvent(event, myDataMapper, myEditor);
|
||||
myDocumentChangedEvent = new IncrementalCacheUpdateEvent(event, myEditor);
|
||||
}
|
||||
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
@@ -1315,7 +1237,6 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
*/
|
||||
@SuppressWarnings("MagicConstant")
|
||||
public void onNewLine() {
|
||||
notifyListenersOnVisualLineEnd();
|
||||
currentPosition.onNewLine();
|
||||
softWrapStartOffset = currentPosition.offset;
|
||||
clearLastFoldInfo();
|
||||
@@ -1365,11 +1286,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
myOffset2widthInPixels.end++;
|
||||
|
||||
int widthInColumns = calculateWidthInColumns(c, metrics[1], myContext.getPlainSpaceWidth());
|
||||
if (c == '\t') {
|
||||
notifyListenersOnVisualLineStart(myContext.lineStartPosition);
|
||||
notifyListenersOnTabulation(widthInColumns);
|
||||
}
|
||||
|
||||
|
||||
currentPosition.logicalColumn += widthInColumns;
|
||||
currentPosition.visualColumn += widthInColumns;
|
||||
currentPosition.x = metrics[0];
|
||||
@@ -1411,22 +1328,14 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
private void advance(FoldRegion foldRegion, int placeHolderWidthInPixels) {
|
||||
lastFoldStartPosition = currentPosition.clone();
|
||||
lastFold = foldRegion;
|
||||
int visualLineBefore = currentPosition.visualLine;
|
||||
int logicalLineBefore = currentPosition.logicalLine;
|
||||
int logicalColumnBefore = currentPosition.logicalColumn;
|
||||
currentPosition.advance(foldRegion, -1);
|
||||
currentPosition.x += placeHolderWidthInPixels;
|
||||
int collapsedFoldingWidthInColumns = currentPosition.logicalColumn;
|
||||
if (currentPosition.logicalLine <= logicalLineBefore) {
|
||||
// Single-line fold region.
|
||||
collapsedFoldingWidthInColumns = currentPosition.logicalColumn - logicalColumnBefore;
|
||||
}
|
||||
else {
|
||||
if (currentPosition.logicalLine > logicalLineBefore) {
|
||||
final DocumentEx document = myEditor.getDocument();
|
||||
int endFoldLine = document.getLineNumber(foldRegion.getEndOffset());
|
||||
logicalLineData.endLineOffset = document.getLineEndOffset(endFoldLine);
|
||||
}
|
||||
notifyListenersOnFoldRegion(foldRegion, collapsedFoldingWidthInColumns, visualLineBefore);
|
||||
tokenStartOffset = myContext.currentPosition.offset;
|
||||
softWrapStartOffset = foldRegion.getEndOffset();
|
||||
lastFoldEndPosition = currentPosition.clone();
|
||||
@@ -1459,7 +1368,7 @@ public class SoftWrapApplianceManager implements Dumpable {
|
||||
* @return <code>true</code> if given <code>'x'</code> coordinate exceeds visual area's right edge; <code>false</code> otherwise
|
||||
*/
|
||||
public boolean exceedsVisualEdge(int x) {
|
||||
return x > myVisibleAreaWidth || x == myVisibleAreaWidth && !myEditor.myUseNewRendering;
|
||||
return x > myVisibleAreaWidth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-82
@@ -15,92 +15,10 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Defines contract for the entity that is interested in document processing. Such an interest may be imposed, for example,
|
||||
* by necessity to perform caching in order to increase performance.
|
||||
* <p/>
|
||||
* I.e. the main idea of effective document processing is to separate document parsing logic and its clients. This interface
|
||||
* defines contract for those clients.
|
||||
* <p/>
|
||||
* Implementations of this interface are not obliged to be thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 10:19:49 AM
|
||||
*/
|
||||
interface SoftWrapAwareDocumentParsingListener {
|
||||
|
||||
/**
|
||||
* Notifies about editor position that points to the visual line start.
|
||||
* <p/>
|
||||
* Target visual line is assumed to belong to one of the categories below:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>it contains fold region;</li>
|
||||
* <li>it contains tabulation symbols;</li>
|
||||
* <li>it ends with soft wrap;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* @param position position that points to the visual line start
|
||||
*/
|
||||
void onVisualLineStart(@NotNull EditorPosition position);
|
||||
|
||||
/**
|
||||
* Notifies about editor position that points to the visual line end
|
||||
*
|
||||
* @param position position that points to the visual line end
|
||||
*/
|
||||
void onVisualLineEnd(@NotNull EditorPosition position);
|
||||
|
||||
/**
|
||||
* Notifies about processed fold region.
|
||||
*
|
||||
* @param foldRegion processed fold region
|
||||
* @param collapsedFoldingWidthInColumns width in columns of the symbols of the given fold regions
|
||||
* @param visualLine visual line where given fold region is located
|
||||
*/
|
||||
void onCollapsedFoldRegion(@NotNull FoldRegion foldRegion, int collapsedFoldingWidthInColumns, int visualLine);
|
||||
|
||||
/**
|
||||
* Notifies about tabulation symbol encountered during document parsing.
|
||||
* <p/>
|
||||
* Tabulations are treated specially because they may occupy different number of visual columns during representation
|
||||
* at IJ editor.
|
||||
*
|
||||
* @param position tabulation symbol position
|
||||
* @param widthInColumns width in visual columns of the tabulation symbol identified by the given position
|
||||
*/
|
||||
void onTabulation(@NotNull EditorPosition position, int widthInColumns);
|
||||
|
||||
/**
|
||||
* Notifies about soft wrap-introduced virtual line feed.
|
||||
*
|
||||
* @param position position just before soft wrap
|
||||
*/
|
||||
void beforeSoftWrapLineFeed(@NotNull EditorPosition position);
|
||||
|
||||
/**
|
||||
* Notifies about soft wrap-introduced virtual line feed.
|
||||
*
|
||||
* @param position position just after soft wrap
|
||||
*/
|
||||
void afterSoftWrapLineFeed(@NotNull EditorPosition position);
|
||||
|
||||
/**
|
||||
* There is a possible case that document parser steps back to particular offset, e.g. it may encounter a situation
|
||||
* when long line should be soft-wrapped and the most appropriate place for a wrap is located couple of symbols below
|
||||
* the current parsing position.
|
||||
* <p/>
|
||||
* Parsing listener is expected to drop all information for the document segment from given offset and offset used last time
|
||||
*
|
||||
* @param offset offset that will be used as a starting point for document parsing
|
||||
* @param visualLine visual line where given offset is located
|
||||
*/
|
||||
void revertToOffset(int offset, int visualLine);
|
||||
|
||||
/**
|
||||
* Notifies current listener that particular document region re-parsing is about to begin.
|
||||
*
|
||||
|
||||
-29
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -23,34 +22,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
* @since 11/23/11 7:04 PM
|
||||
*/
|
||||
public abstract class SoftWrapAwareDocumentParsingListenerAdapter implements SoftWrapAwareDocumentParsingListener {
|
||||
@Override
|
||||
public void onVisualLineStart(@NotNull EditorPosition position) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisualLineEnd(@NotNull EditorPosition position) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCollapsedFoldRegion(@NotNull FoldRegion foldRegion, int collapsedFoldingWidthInColumns, int visualLine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabulation(@NotNull EditorPosition position, int widthInColumns) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSoftWrapLineFeed(@NotNull EditorPosition position) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSoftWrapLineFeed(@NotNull EditorPosition position) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revertToOffset(int offset, int visualLine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCacheUpdateStart(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
}
|
||||
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.impl.VisualSizeChangeListener;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapPainter;
|
||||
import gnu.trove.TIntIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wraps soft-wraps specific document parsing to the {@link VisualSizeChangeListener} API.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 12/6/10 12:02 PM
|
||||
*/
|
||||
public class SoftWrapAwareVisualSizeManager extends SoftWrapAwareDocumentParsingListenerAdapter {
|
||||
|
||||
private final List<VisualSizeChangeListener> myListeners = new ArrayList<>();
|
||||
private final TIntIntHashMap myLineWidths = new TIntIntHashMap();
|
||||
|
||||
private SoftWrapPainter myPainter;
|
||||
|
||||
private int myLastLogicalLine;
|
||||
|
||||
public SoftWrapAwareVisualSizeManager(@NotNull SoftWrapPainter painter) {
|
||||
myPainter = painter;
|
||||
}
|
||||
|
||||
public boolean addVisualSizeChangeListener(@NotNull VisualSizeChangeListener listener) {
|
||||
return myListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCacheUpdateStart(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
myLineWidths.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecalculationEnd(@NotNull IncrementalCacheUpdateEvent event) {
|
||||
if (myListeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int startLine = event.getStartLogicalLine();
|
||||
int oldEndLine = event.getOldEndLogicalLine();
|
||||
for (VisualSizeChangeListener listener : myListeners) {
|
||||
listener.onLineWidthsChange(startLine, oldEndLine, myLastLogicalLine, myLineWidths);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisualLineEnd(@NotNull EditorPosition position) {
|
||||
if (myListeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
updateLineWidthIfNecessary(position.logicalLine, position.x);
|
||||
myLastLogicalLine = position.logicalLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSoftWrapLineFeed(@NotNull EditorPosition position) {
|
||||
int newWidth = position.x + myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
|
||||
myLastLogicalLine = position.logicalLine;
|
||||
if (!myLineWidths.contains(position.logicalLine)) {
|
||||
myLineWidths.put(position.logicalLine, newWidth);
|
||||
return;
|
||||
}
|
||||
|
||||
updateLineWidthIfNecessary(position.logicalLine, newWidth);
|
||||
}
|
||||
|
||||
private void updateLineWidthIfNecessary(int line, int width) {
|
||||
int storedWidth = myLineWidths.get(line);
|
||||
if (width > storedWidth) {
|
||||
myLineWidths.put(line, width);
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void setSoftWrapPainter(SoftWrapPainter painter) {
|
||||
myPainter = painter;
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
/**
|
||||
* {@link CompositeDataProvider} defines a generic infrastructure for combining multiple {@link DataProvider}s to the
|
||||
* single compound entity. But it should be possible to distinguish the source of
|
||||
* {@link CompositeDataProvider#getData() every data} returned from it.
|
||||
* <p/>
|
||||
* Current enum defines types of sources which data is used during soft wraps calculation.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 12:23:04 PM
|
||||
*/
|
||||
public enum SoftWrapDataProviderKeys {
|
||||
|
||||
// Please note that declaration order pays significant role here. It defines processing order, i.e. soft wraps are processed before,
|
||||
// say, collapsed fold regions located at the same offset.
|
||||
SOFT_WRAP,
|
||||
COLLAPSED_FOLDING,
|
||||
TABULATION
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.SoftWrap;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* {@link DataProvider} implementation that exposes information about {@link SoftWrap soft wraps}.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 31, 2010 12:18:49 PM
|
||||
*/
|
||||
public class SoftWrapsDataProvider extends AbstractListBasedDataProvider<SoftWrapDataProviderKeys, SoftWrapImpl> {
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public SoftWrapsDataProvider(@NotNull SoftWrapsStorage storage) {
|
||||
super(SoftWrapDataProviderKeys.SOFT_WRAP, storage.getSoftWraps());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSortingKey(@NotNull SoftWrapImpl data) {
|
||||
return data.getStart();
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 9, 2010 9:21:22 AM
|
||||
*/
|
||||
class TabData implements Cloneable {
|
||||
|
||||
public final int widthInColumns;
|
||||
public int offset;
|
||||
|
||||
TabData(int widthInColumns, int offset) {
|
||||
this.widthInColumns = widthInColumns;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + offset + ", width: " + widthInColumns + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TabData clone() {
|
||||
return new TabData(widthInColumns, offset);
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 9, 2010 9:21:34 AM
|
||||
*/
|
||||
class TabulationDataProvider extends AbstractListBasedDataProvider<SoftWrapDataProviderKeys, TabData> {
|
||||
|
||||
TabulationDataProvider(@NotNull List<TabData> data) {
|
||||
super(SoftWrapDataProviderKeys.TABULATION, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSortingKey(@NotNull TabData data) {
|
||||
return data.offset;
|
||||
}
|
||||
}
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since Sep 9, 2010 9:20:44 AM
|
||||
*/
|
||||
class VisualToLogicalCalculationStrategy extends AbstractMappingStrategy<LogicalPosition> {
|
||||
|
||||
private final CacheEntry mySearchKey;
|
||||
private VisualPosition myTargetVisual;
|
||||
|
||||
VisualToLogicalCalculationStrategy(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull List<CacheEntry> cache)
|
||||
{
|
||||
super(editor, storage, cache);
|
||||
mySearchKey = new CacheEntry(0, editor);
|
||||
}
|
||||
|
||||
public void init(@NotNull final VisualPosition targetVisual, @NotNull final List<CacheEntry> cache) {
|
||||
reset();
|
||||
|
||||
myTargetVisual = targetVisual;
|
||||
mySearchKey.visualLine = targetVisual.line;
|
||||
int i = Collections.binarySearch(cache, mySearchKey);
|
||||
if (i >= 0) {
|
||||
CacheEntry cacheEntry = cache.get(i);
|
||||
if (cacheEntry.visualLine == targetVisual.line) {
|
||||
if (targetVisual.column == 0) {
|
||||
setEagerMatch(cacheEntry.buildStartLinePosition().buildLogicalPosition());
|
||||
}
|
||||
else if (targetVisual.column == cacheEntry.endVisualColumn) {
|
||||
setEagerMatch(cacheEntry.buildEndLinePosition().buildLogicalPosition());
|
||||
}
|
||||
else if (targetVisual.column > cacheEntry.endVisualColumn) {
|
||||
EditorPosition position = cacheEntry.buildEndLinePosition();
|
||||
int columnsDiff = targetVisual.column - cacheEntry.endVisualColumn;
|
||||
position.visualColumn += columnsDiff;
|
||||
if (myStorage.getSoftWrap(cacheEntry.endOffset) == null) {
|
||||
position.logicalColumn += columnsDiff;
|
||||
}
|
||||
else {
|
||||
position.softWrapColumnDiff += columnsDiff;
|
||||
}
|
||||
setEagerMatch(position.buildLogicalPosition());
|
||||
}
|
||||
else {
|
||||
setTargetEntry(cacheEntry, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setInitialPosition(cacheEntry.buildStartLinePosition());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
i = -i - 1;
|
||||
if (i > 0 && i <= cache.size()) {
|
||||
CacheEntry entry = cache.get(i - 1);
|
||||
EditorPosition position = entry.buildEndLinePosition();
|
||||
position.onNewLineSoftWrapAware();
|
||||
setInitialPosition(position);
|
||||
}
|
||||
else {
|
||||
setFirstInitialPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LogicalPosition buildIfExceeds(EditorPosition position, int offset) {
|
||||
Document document = myEditor.getDocument();
|
||||
|
||||
// There is a possible case that target visual line starts with soft wrap. We want to process that at 'processSoftWrap()' method.
|
||||
CacheEntry targetEntry = getTargetEntry();
|
||||
if (targetEntry != null && targetEntry.startOffset == offset && myStorage.getSoftWrap(offset) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int i = MappingUtil.getCacheEntryIndexForOffset(offset, document, myCache);
|
||||
if (i < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheEntry cacheEntry = myCache.get(i);
|
||||
if (cacheEntry.visualLine < myTargetVisual.line) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return eagerly if target visual position remains between current context position and the one defined by the given offset.
|
||||
|
||||
if (offset < cacheEntry.startOffset || myTargetVisual.line < cacheEntry.visualLine
|
||||
|| (position.visualColumn + offset - position.offset >= myTargetVisual.column))
|
||||
{
|
||||
int linesDiff = myTargetVisual.line - position.visualLine;
|
||||
if (linesDiff > 0) {
|
||||
position.logicalColumn = myTargetVisual.column;
|
||||
position.offset = document.getLineStartOffset(position.logicalLine + linesDiff);
|
||||
position.offset += myTargetVisual.column;
|
||||
}
|
||||
else {
|
||||
int columnsDiff = myTargetVisual.column - position.visualColumn;
|
||||
position.logicalColumn += columnsDiff;
|
||||
position.offset += columnsDiff;
|
||||
}
|
||||
|
||||
// There is a possible case that target visual position points to virtual space after line end. We need
|
||||
// to define correct offset then
|
||||
position.offset = Math.min(position.offset, document.getLineEndOffset(position.logicalLine));
|
||||
|
||||
position.visualLine += linesDiff;
|
||||
position.visualColumn = myTargetVisual.column;
|
||||
position.logicalLine += linesDiff;
|
||||
return position.buildLogicalPosition();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LogicalPosition processSoftWrap(@NotNull EditorPosition position, SoftWrap softWrap) {
|
||||
// There is a possible case that current visual line starts with soft wrap and target visual position points to its
|
||||
// virtual space.
|
||||
CacheEntry targetEntry = getTargetEntry();
|
||||
if (targetEntry != null && targetEntry.startOffset == softWrap.getStart()) {
|
||||
if (myTargetVisual.column <= softWrap.getIndentInColumns()) {
|
||||
EditorPosition resultingContext = targetEntry.buildStartLinePosition();
|
||||
resultingContext.visualColumn = myTargetVisual.column;
|
||||
resultingContext.softWrapColumnDiff += myTargetVisual.column;
|
||||
return resultingContext.buildLogicalPosition();
|
||||
}
|
||||
else {
|
||||
position.visualColumn = softWrap.getIndentInColumns();
|
||||
position.softWrapColumnDiff += softWrap.getIndentInColumns();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LogicalPosition buildIfExceeds(@NotNull EditorPosition context, @NotNull FoldRegion foldRegion) {
|
||||
// We assume that fold region placeholder contains only 'simple' symbols, i.e. symbols that occupy single visual column.
|
||||
String placeholder = foldRegion.getPlaceholderText();
|
||||
|
||||
// Check if target visual position points inside collapsed fold region placeholder
|
||||
if (myTargetVisual.column < /* note that we don't use <= here */ context.visualColumn + placeholder.length()) {
|
||||
// Map all visual positions that point inside collapsed fold region as the logical position of it's start.
|
||||
return context.buildLogicalPosition();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LogicalPosition buildIfExceeds(EditorPosition context, TabData tabData) {
|
||||
if (context.visualColumn + tabData.widthInColumns >= myTargetVisual.column) {
|
||||
context.logicalColumn += myTargetVisual.column - context.visualColumn;
|
||||
context.visualColumn = myTargetVisual.column;
|
||||
return context.buildLogicalPosition();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition build(@NotNull EditorPosition position) {
|
||||
int linesDiff = myTargetVisual.line - position.visualLine;
|
||||
if (linesDiff <= 0) {
|
||||
int columnsDiff = myTargetVisual.column - position.visualColumn;
|
||||
position.logicalColumn += columnsDiff;
|
||||
position.offset += columnsDiff;
|
||||
}
|
||||
else {
|
||||
position.logicalLine += linesDiff;
|
||||
position.visualLine += linesDiff;
|
||||
Document document = myEditor.getDocument();
|
||||
if (document.getLineCount() <= position.logicalLine) {
|
||||
position.offset = Math.max(0, document.getTextLength() - 1);
|
||||
}
|
||||
else {
|
||||
int startLineOffset = document.getLineStartOffset(position.logicalLine);
|
||||
position.offset = startLineOffset + myTargetVisual.column;
|
||||
}
|
||||
position.logicalColumn = myTargetVisual.column;
|
||||
}
|
||||
|
||||
position.visualColumn = myTargetVisual.column;
|
||||
return position.buildLogicalPosition();
|
||||
}
|
||||
}
|
||||
-3
@@ -21,7 +21,6 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
import com.intellij.openapi.fileTypes.BinaryFileTypeDecompilers;
|
||||
@@ -31,7 +30,6 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.SingleRootFileViewProvider;
|
||||
@@ -211,7 +209,6 @@ public class TextEditorProvider implements FileEditorProvider, DumbAware {
|
||||
|
||||
protected TextEditorState getStateImpl(final Project project, @NotNull Editor editor, @NotNull FileEditorStateLevel level){
|
||||
TextEditorState state = new TextEditorState();
|
||||
if (!Registry.is("editor.new.rendering") && editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) return state;
|
||||
CaretModel caretModel = editor.getCaretModel();
|
||||
if (caretModel.supportsMultipleCarets()) {
|
||||
List<CaretState> caretsAndSelections = caretModel.getCaretsAndSelections();
|
||||
|
||||
-116
@@ -17,12 +17,7 @@ package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.FoldingModelEx;
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.CachingSoftWrapDataMapper;
|
||||
import com.intellij.openapi.editor.impl.view.FontLayoutService;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.testFramework.EditorTestUtil;
|
||||
import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
|
||||
import com.intellij.testFramework.MockFontLayoutService;
|
||||
@@ -36,8 +31,6 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -128,115 +121,6 @@ public abstract class AbstractEditorTest extends LightPlatformCodeInsightTestCas
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups document of the {@link #getEditor() current editor} according to the given text that is expected to contain
|
||||
* information about document lines obtained from the {@link DocumentImpl#dumpState()}.
|
||||
*
|
||||
* @param data string representation of the target document's lines
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
protected static void setupDocument(@NotNull String data) {
|
||||
Scanner scanner = new Scanner(data);
|
||||
Pattern pattern = Pattern.compile("(\\d+)\\s*:\\s*(\\d+)\\s*-\\s*(\\d+)");
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
while (scanner.findInLine(pattern) != null) {
|
||||
final MatchResult match = scanner.match();
|
||||
int startOffset = Integer.parseInt(match.group(2));
|
||||
int endOffset = Integer.parseInt(match.group(3));
|
||||
buffer.append(StringUtil.repeatSymbol('a', endOffset - startOffset)).append('\n');
|
||||
}
|
||||
if (buffer.length() > 0) {
|
||||
buffer.setLength(buffer.length() - 1);
|
||||
}
|
||||
myEditor.getDocument().setText(buffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups {@link Editor#getFoldingModel() folding model} of the {@link #getEditor() current editor} according to the given text
|
||||
* that is expected to contain information obtained from the {@link FoldingModelImpl#toString()}.
|
||||
*
|
||||
* @param data string representation of the target fold regions
|
||||
*/
|
||||
protected static void setupFolding(@NotNull String data) {
|
||||
Scanner scanner = new Scanner(data);
|
||||
Pattern pattern = Pattern.compile("FoldRegion ([+-])\\((\\d+):(\\d+)");
|
||||
final List<Trinity<Boolean, Integer, Integer>> infos = new ArrayList<>();
|
||||
while (scanner.findInLine(pattern) != null) {
|
||||
final MatchResult match = scanner.match();
|
||||
boolean expanded = "-".equals(match.group(1));
|
||||
int startOffset = Integer.parseInt(match.group(2));
|
||||
int endOffset = Integer.parseInt(match.group(3));
|
||||
infos.add(new Trinity<>(expanded, startOffset, endOffset));
|
||||
}
|
||||
final FoldingModel foldingModel = myEditor.getFoldingModel();
|
||||
foldingModel.runBatchFoldingOperation(() -> {
|
||||
for (Trinity<Boolean, Integer, Integer> info : infos) {
|
||||
final FoldRegion region = foldingModel.addFoldRegion(info.second, info.third, "...");
|
||||
assert region != null;
|
||||
region.setExpanded(info.first);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups {@link Editor#getSoftWrapModel() soft wraps model} of the {@link #getEditor() current editor} according to the given text
|
||||
* that is expected to contain information obtained from the {@link CachingSoftWrapDataMapper#toString()}.
|
||||
*
|
||||
* @param data string representation of the target soft wraps cache
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
protected static void setupSoftWraps(@NotNull String data) {
|
||||
Scanner scanner = new Scanner(data);
|
||||
Pattern generalPattern =
|
||||
Pattern.compile("visual line: (\\d+), offsets: (\\d+)-(\\d+), logical lines: (\\d+)-(\\d+), logical columns: (\\d+)-(\\d+), "
|
||||
+ "end visual column: (\\d+), fold regions: \\[([^\\]]*)\\], tab data: \\[([^\\]]*)\\]");
|
||||
Pattern foldPattern = Pattern.compile("width in columns: (-?\\d+), fold region: FoldRegion [-+]\\((\\d+):(\\d+)");
|
||||
Pattern tabPattern = Pattern.compile("\\[(\\d+), width: (\\d+)");
|
||||
final SoftWrapModelImpl softWrapModel = (SoftWrapModelImpl)myEditor.getSoftWrapModel();
|
||||
final CachingSoftWrapDataMapper mapper = softWrapModel.getDataMapper();
|
||||
mapper.release();
|
||||
final FoldingModelEx foldingModel = (FoldingModelEx)myEditor.getFoldingModel();
|
||||
while (scanner.findInLine(generalPattern) != null) {
|
||||
final MatchResult generalMatch = scanner.match();
|
||||
int visualLine = Integer.parseInt(generalMatch.group(1));
|
||||
int startOffset = Integer.parseInt(generalMatch.group(2));
|
||||
int endOffset = Integer.parseInt(generalMatch.group(3));
|
||||
int startLogicalLine = Integer.parseInt(generalMatch.group(4));
|
||||
int endLogicalLine = Integer.parseInt(generalMatch.group(5));
|
||||
int startLogicalColumn = Integer.parseInt(generalMatch.group(6));
|
||||
int endLogicalColumn = Integer.parseInt(generalMatch.group(7));
|
||||
int endVisualColumn = Integer.parseInt(generalMatch.group(8));
|
||||
|
||||
List<Pair<Integer, FoldRegion>> foldRegions = new ArrayList<>();
|
||||
Scanner foldScanner = new Scanner(generalMatch.group(9));
|
||||
while (foldScanner.findInLine(foldPattern) != null) {
|
||||
final MatchResult foldMatch = foldScanner.match();
|
||||
int widthInColumns = Integer.parseInt(foldMatch.group(1));
|
||||
int foldStartOffset = Integer.parseInt(foldMatch.group(2));
|
||||
int foldEndOffset = Integer.parseInt(foldMatch.group(3));
|
||||
FoldRegion region = null;
|
||||
for (FoldRegion candidate : foldingModel.getAllFoldRegions()) {
|
||||
if (candidate.getStartOffset() == foldStartOffset && candidate.getEndOffset() == foldEndOffset) {
|
||||
region = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foldRegions.add(new Pair<>(widthInColumns, region));
|
||||
}
|
||||
|
||||
List<Pair<Integer, Integer>> tabData = new ArrayList<>();
|
||||
Scanner tabScanner = new Scanner(generalMatch.group(10));
|
||||
while (tabScanner.findInLine(tabPattern) != null) {
|
||||
final MatchResult tabMatch = tabScanner.match();
|
||||
int offset = Integer.parseInt(tabMatch.group(1));
|
||||
int widthInColumns = Integer.parseInt(tabMatch.group(2));
|
||||
tabData.add(new Pair<>(offset, widthInColumns));
|
||||
}
|
||||
|
||||
mapper.rawAdd(visualLine, startOffset, endOffset, startLogicalLine, startLogicalColumn, endLogicalLine, endLogicalColumn, endVisualColumn, foldRegions, tabData);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertSelectionRanges(int[][] ranges) {
|
||||
int[] selectionStarts = myEditor.getSelectionModel().getBlockSelectionStarts();
|
||||
int[] selectionEnds = myEditor.getSelectionModel().getBlockSelectionEnds();
|
||||
|
||||
-890
@@ -1,890 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.mock.MockFoldRegion;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.FoldingModelEx;
|
||||
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
|
||||
import com.intellij.openapi.editor.impl.TextChangeImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.api.Invocation;
|
||||
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||
import org.jmock.lib.action.CustomAction;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 07/07/2010
|
||||
*/
|
||||
public class CachingSoftWrapDataMapperTest {
|
||||
|
||||
private static final Comparator<DataEntry> LOGICAL_POSITIONS_COMPARATOR = (o1, o2) -> {
|
||||
LogicalPosition logical1 = o1.logical;
|
||||
LogicalPosition logical2 = o2.logical;
|
||||
if (logical1.line != logical2.line) {
|
||||
return logical1.line - logical2.line;
|
||||
}
|
||||
|
||||
// There is a possible case that multiple logical positions match to the same visual position (e.g. logical
|
||||
// positions for folded text match to the same visual position). We want to match to the logical position of
|
||||
// folding region start if we search by logical position from folded text.
|
||||
if (o1.foldedSpace && o2.foldedSpace && logical1.column + logical1.foldingColumnDiff == logical2.foldingColumnDiff) {
|
||||
return o1.foldedSpace ? 1 : -1;
|
||||
}
|
||||
return logical1.column - logical2.column;
|
||||
};
|
||||
|
||||
private static final Comparator<DataEntry> OFFSETS_COMPARATOR = (o1, o2) -> {
|
||||
if (o1.offset != o2.offset) {
|
||||
return o1.offset - o2.offset;
|
||||
}
|
||||
// There are numerous situations when multiple visual positions share the same offset (e.g. all soft wrap-introduced virtual
|
||||
// spaces share offset with the first document symbol after soft wrap or all virtual spaces after line end share the same offset
|
||||
// as the last line symbol). We want to ignore such positions during lookup by offset.
|
||||
if (o1.virtualSpace ^ o2.virtualSpace) {
|
||||
return o1.virtualSpace ? 1 : -1;
|
||||
}
|
||||
if (o1.insideTab ^ o2.insideTab) {
|
||||
return o1.insideTab ? 1 : -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
private static final String SOFT_WRAP_START_MARKER = "<WRAP>";
|
||||
private static final String SOFT_WRAP_END_MARKER = "</WRAP>";
|
||||
private static final String FOLDING_START_MARKER = "<FOLD>";
|
||||
private static final String FOLDING_END_MARKER = "</FOLD>";
|
||||
private static final int TAB_SIZE = 4;
|
||||
private static final int SPACE_SIZE = 7;
|
||||
private static final int SOFT_WRAP_DRAWING_WIDTH = 11;
|
||||
|
||||
/** Holds expected mappings between visual and logical positions and offset. */
|
||||
private final List<DataEntry> myExpectedData = new ArrayList<>();
|
||||
private final List<TextRange> myLineRanges = new ArrayList<>();
|
||||
/** Holds document offsets that are considered to be folded. */
|
||||
private final TIntHashSet myFoldedOffsets = new TIntHashSet();
|
||||
private final List<FoldRegion> myFoldRegions = new ArrayList<>();
|
||||
|
||||
private CachingSoftWrapDataMapper myMapper;
|
||||
private Mockery myMockery;
|
||||
private EditorEx myEditor;
|
||||
private DocumentEx myDocument;
|
||||
private StringBuilder myChars;
|
||||
private SoftWrapsStorage myStorage;
|
||||
private SoftWrapModelEx mySoftWrapModel;
|
||||
private FoldingModelEx myFoldingModel;
|
||||
private MockEditorTextRepresentationHelper myRepresentationHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
myMockery = new JUnit4Mockery();
|
||||
|
||||
myEditor = myMockery.mock(EditorEx.class);
|
||||
myDocument = myMockery.mock(DocumentEx.class);
|
||||
myChars = new StringBuilder();
|
||||
myStorage = new SoftWrapsStorage();
|
||||
mySoftWrapModel = myMockery.mock(SoftWrapModelEx.class);
|
||||
myFoldingModel = myMockery.mock(FoldingModelEx.class);
|
||||
final EditorSettings settings = myMockery.mock(EditorSettings.class);
|
||||
final Project project = myMockery.mock(Project.class);
|
||||
final SoftWrapPainter painter = myMockery.mock(SoftWrapPainter.class);
|
||||
|
||||
myRepresentationHelper = new MockEditorTextRepresentationHelper(myChars, SPACE_SIZE, TAB_SIZE);
|
||||
|
||||
myMockery.checking(new Expectations() {{
|
||||
// Document
|
||||
allowing(myEditor).getDocument();will(returnValue(myDocument));
|
||||
allowing(myDocument).getLineCount(); will(new CustomAction("getLineCount()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myLineRanges.size();
|
||||
}
|
||||
});
|
||||
allowing(myDocument).getLineNumber(with(any(int.class))); will(new CustomAction("getLineNumber()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getLineNumber((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myDocument).getLineStartOffset(with(any(int.class))); will(new CustomAction("getLineStart()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getLineStartOffset((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myDocument).getLineEndOffset(with(any(int.class))); will(new CustomAction("getLineEnd()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getLineEndOffset((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
|
||||
// Settings.
|
||||
allowing(myEditor).getSettings();will(returnValue(settings));
|
||||
allowing(settings).isUseSoftWraps();will(returnValue(true));
|
||||
allowing(settings).getTabSize(project);will(returnValue(TAB_SIZE));
|
||||
allowing(settings).isWhitespacesShown();will(returnValue(true));
|
||||
allowing(myEditor).getProject();will(returnValue(project));
|
||||
|
||||
// Soft wraps.
|
||||
allowing(myEditor).getSoftWrapModel(); will(returnValue(mySoftWrapModel));
|
||||
allowing(mySoftWrapModel).getSoftWrap(with(any(int.class))); will(new CustomAction("getSoftWrap") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getSoftWrap((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(mySoftWrapModel).getEditorTextRepresentationHelper(); will(returnValue(myRepresentationHelper));
|
||||
|
||||
// Folding.
|
||||
allowing(myEditor).getFoldingModel();will(returnValue(myFoldingModel));
|
||||
allowing(myFoldingModel).isFoldingEnabled(); returnValue(true);
|
||||
allowing(myFoldingModel).setFoldingEnabled(with(any(boolean.class)));
|
||||
allowing(myFoldingModel).isOffsetCollapsed(with(any(int.class))); will(new CustomAction("isOffsetCollapsed()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myFoldedOffsets.contains((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myFoldingModel).getCollapsedRegionAtOffset(with(any(int.class))); will(new CustomAction("getCollapsedRegionAtOffset()") {
|
||||
@Nullable
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getCollapsedFoldRegion((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myFoldingModel).fetchTopLevel(); will(new CustomAction("fetchTopLevel()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myFoldRegions.toArray(new FoldRegion[myFoldRegions.size()]);
|
||||
}
|
||||
});
|
||||
|
||||
// Soft wrap-unaware conversions.
|
||||
allowing(myEditor).logicalToVisualPosition(with(any(LogicalPosition.class))); will(new CustomAction("logical2visual()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return logicalToVisual((LogicalPosition)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myEditor).logicalPositionToOffset(with(any(LogicalPosition.class))); will(new CustomAction("logical2offset()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return logicalToOffset((LogicalPosition)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myEditor).offsetToLogicalPosition(with(any(int.class))); will(new CustomAction("offset2logical()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return offsetToLogical((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
|
||||
// Soft wrap painter.
|
||||
allowing(painter).getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); will(returnValue(SOFT_WRAP_DRAWING_WIDTH));
|
||||
}});
|
||||
|
||||
myMapper = new CachingSoftWrapDataMapper(myEditor, myStorage);
|
||||
}
|
||||
|
||||
@After
|
||||
public void checkExpectations() {
|
||||
myMockery.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noSoftWrapsAndFolding() {
|
||||
String document =
|
||||
"class Test { \n" +
|
||||
" public void foo() {} \n" +
|
||||
" \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleSoftWrap() {
|
||||
String document =
|
||||
"class Test { \n" +
|
||||
" public void <WRAP>\n" +
|
||||
" </WRAP>foo() {\n" +
|
||||
" } \n" +
|
||||
" \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSoftWrappedLogicalLines() {
|
||||
String document =
|
||||
"public class Test {\n" +
|
||||
" public void foo(int[] data) {\n" +
|
||||
" bar(data[0], <WRAP>\n" +
|
||||
" </WRAP>data[1] <WRAP>\n" +
|
||||
" </WRAP>data[2] \n" +
|
||||
" data[3], \n" +
|
||||
" data[4], <WRAP>\n" +
|
||||
" </WRAP>data[5] <WRAP>\n" +
|
||||
" </WRAP>data[6]); \n" +
|
||||
" }\n" +
|
||||
" public void bar(int ... i) {\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleLineFolding() {
|
||||
String document =
|
||||
"class Test {\n" +
|
||||
" Map<String, Integer> map = new HashMap<FOLD><String, Integer></FOLD>(); \n" +
|
||||
" \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoLinesFolding() {
|
||||
String document =
|
||||
"class Test {\n" +
|
||||
" public void foo() {<FOLD>\n" +
|
||||
" }</FOLD>\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeLinesFolding() {
|
||||
String document =
|
||||
"class Test {\n" +
|
||||
" public void foo() {<FOLD>\n" +
|
||||
" int i = 1; \n" +
|
||||
" }</FOLD>\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void softWrappedSingleLineFolding() {
|
||||
String document =
|
||||
"class Test {<WRAP>\n" +
|
||||
" </WRAP><FOLD>public void foo() {}</FOLD> \n" +
|
||||
" \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void softWrappedMultiLineLineFolding() {
|
||||
String document =
|
||||
"class Test {<WRAP>\n" +
|
||||
" </WRAP><FOLD>public void foo() {\n" +
|
||||
" }</FOLD> \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleFoldRegionsAfterSingleSoftWrap() {
|
||||
String document =
|
||||
"class Test {<WRAP>\n" +
|
||||
" </WRAP><FOLD>public void foo() {\n" +
|
||||
" }</FOLD> <FOLD>// comment</FOLD> \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void softWrapAndFoldedLines() {
|
||||
String document =
|
||||
"public class Test {\n" +
|
||||
" public void foo(int[] data) {\n" +
|
||||
" bar(data[0] <WRAP>\n" +
|
||||
" </WRAP>data[1], <WRAP>\n" +
|
||||
" </WRAP>data[2], \n" +
|
||||
" data[3], \n" +
|
||||
" <FOLD>data[4], \n" +
|
||||
" data[5], \n" +
|
||||
" data[6], </FOLD> \n" +
|
||||
" data[7], \n" +
|
||||
" data[8] <WRAP>\n" +
|
||||
" </WRAP>data[9], <WRAP>\n" +
|
||||
" </WRAP>data[10]); \n" +
|
||||
" }\n" +
|
||||
" public void bar(int ... i) {\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consecutiveFoldRegions() {
|
||||
String document =
|
||||
"package org;\n" +
|
||||
"\n" +
|
||||
"import <FOLD>java.util.List;\n" +
|
||||
"import java.util.ArrayList;\n" +
|
||||
"import java.util.LinkedList;</FOLD>\n" +
|
||||
"<FOLD>/**\n" +
|
||||
" * @author Vasiliy\n" +
|
||||
" */</FOLD>\n" +
|
||||
"public class Test {\n" +
|
||||
" <FOLD>/**\n" +
|
||||
" * Method-level javadoc\n" +
|
||||
" */</FOLD>\n" +
|
||||
" public void test() {\n" +
|
||||
" }" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tabSymbolsBeforeSoftWrap() {
|
||||
String document =
|
||||
"class Test\t\t{<WRAP>\n" +
|
||||
" </WRAP> \n}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tabSymbolsAfterSoftWrap() {
|
||||
String document =
|
||||
"class Test {<WRAP>\n" +
|
||||
" </WRAP> \t\t\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleTabsAndSoftWraps() {
|
||||
String document =
|
||||
"public class \tTest {\n" +
|
||||
" public void foo(int[] data) {\n" +
|
||||
" bar(data[0], data[1],\t\t <WRAP>\n" +
|
||||
" </WRAP>data[2], data[3], <WRAP>\n" +
|
||||
" </WRAP>data[4], data[5],\t \t \n" +
|
||||
" data[6], data[7],\t \t \n" +
|
||||
" data[8], data[9],\t \t <WRAP>\n" +
|
||||
" </WRAP>data[10], data[11], <WRAP>\n" +
|
||||
" </WRAP>data[12],\t \t data[13]); \n" +
|
||||
" }\n" +
|
||||
" public void bar(int ... i) {\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tabBeforeFolding() {
|
||||
|
||||
String document =
|
||||
"class Test\t \t <FOLD>{\n" +
|
||||
" </FOLD> \t\t\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleTabsAndFolding() {
|
||||
String document =
|
||||
"public class \tTest {\n" +
|
||||
" public void foo(int[] data) {\n" +
|
||||
" bar(data[0], data[1],\t\t <FOLD>\n" +
|
||||
" \t \t </FOLD>data[2], data[3]\n" +
|
||||
" data[4], data[5],\t \t \n" +
|
||||
" data[6], data[7],\t \t <FOLD>\n" +
|
||||
"\t \t </FOLD>); \n" +
|
||||
" }\n" +
|
||||
" public void bar(int ... i) {\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
private int getLineNumber(int offset) {
|
||||
int line = 0;
|
||||
for (TextRange range : myLineRanges) {
|
||||
if (offset >= range.getStartOffset() && offset <= range.getEndOffset()) {
|
||||
return line;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
return line + 1;
|
||||
}
|
||||
|
||||
private int getLineStartOffset(int line) {
|
||||
checkLine(line);
|
||||
return myLineRanges.get(line).getStartOffset();
|
||||
}
|
||||
|
||||
private int getLineEndOffset(int line) {
|
||||
checkLine(line);
|
||||
return myLineRanges.get(line).getEndOffset();
|
||||
}
|
||||
|
||||
private void checkLine(int line) {
|
||||
if (line < 0 || line >= myLineRanges.size()) {
|
||||
throw new AssertionError(String.format("Can't retrieve target data for the given line (%d). Reason - it's not within allowed "
|
||||
+ "bounds ([0; %d])", line, myLineRanges.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SoftWrap getSoftWrap(int offset) {
|
||||
return myStorage.getSoftWrap(offset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FoldRegion getCollapsedFoldRegion(int offset) {
|
||||
for (FoldRegion region : myFoldRegions) {
|
||||
if (region.getStartOffset() <= offset && region.getEndOffset() > offset) {
|
||||
return region;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private VisualPosition logicalToVisual(LogicalPosition position) {
|
||||
DataEntry dataEntry = myExpectedData.get(findIndex(new DataEntry(null, position, 0, false), LOGICAL_POSITIONS_COMPARATOR));
|
||||
return toSoftWrapUnawareVisual(dataEntry);
|
||||
}
|
||||
|
||||
private int logicalToOffset(LogicalPosition position) {
|
||||
DataEntry dataEntry = myExpectedData.get(findIndex(new DataEntry(null, position, 0, false), LOGICAL_POSITIONS_COMPARATOR));
|
||||
return dataEntry.offset;
|
||||
}
|
||||
|
||||
private LogicalPosition offsetToLogical(int offset) {
|
||||
DataEntry dataEntry = myExpectedData.get(findIndex(new DataEntry(null, null, offset, false), OFFSETS_COMPARATOR));
|
||||
return toVisualPositionUnawareLogical(dataEntry);
|
||||
}
|
||||
|
||||
private LogicalPosition offsetToSoftWrapUnawareLogical(int offset) {
|
||||
DataEntry dataEntry = myExpectedData.get(findIndex(new DataEntry(null, null, offset, false), OFFSETS_COMPARATOR));
|
||||
return toVisualPositionUnawareLogical(dataEntry);
|
||||
}
|
||||
|
||||
private int findIndex(DataEntry key, Comparator<DataEntry> comparator) {
|
||||
int i = Collections.binarySearch(myExpectedData, key, comparator);
|
||||
if (i < 0 || i >= myExpectedData.size()) {
|
||||
throw new AssertionError(String.format("Can't find pre-configured data entry for the given key (%s). "
|
||||
+ "Available data: %s", key, myExpectedData));
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private static VisualPosition toSoftWrapUnawareVisual(DataEntry dataEntry) {
|
||||
LogicalPosition logical = dataEntry.logical;
|
||||
return new VisualPosition(logical.line - logical.foldedLines, logical.column + logical.foldingColumnDiff);
|
||||
}
|
||||
|
||||
private static LogicalPosition toSoftWrapUnawareLogicalByVisual(DataEntry dataEntry) {
|
||||
LogicalPosition logical = dataEntry.logical;
|
||||
return new LogicalPosition(
|
||||
logical.line + logical.softWrapLinesBeforeCurrentLogicalLine + logical.softWrapLinesOnCurrentLogicalLine,
|
||||
logical.column
|
||||
);
|
||||
}
|
||||
|
||||
private static LogicalPosition toVisualPositionUnawareLogical(DataEntry dataEntry) {
|
||||
LogicalPosition logical = dataEntry.logical;
|
||||
return new LogicalPosition(logical.line, logical.column);
|
||||
}
|
||||
|
||||
private void test(String documentText) {
|
||||
init(documentText);
|
||||
for (DataEntry data : myExpectedData) {
|
||||
|
||||
// Check logical by visual.
|
||||
LogicalPosition actualLogicalByVisual = myMapper.visualToLogical(data.visual);
|
||||
// We don't want to perform the check for logical positions that correspond to the folded space because all of them relate to
|
||||
// the same logical position of the folding start.
|
||||
if (!data.foldedSpace && !data.insideTab && !equals(data.logical, actualLogicalByVisual)) {
|
||||
throw new AssertionError(
|
||||
String.format("Detected unmatched logical position by visual (%s). Expected: '%s', actual: '%s'. Calculation was performed "
|
||||
+ "against soft wrap-unaware logical: '%s'",
|
||||
data.visual, data.logical, actualLogicalByVisual, toSoftWrapUnawareLogicalByVisual(data))
|
||||
);
|
||||
}
|
||||
|
||||
// Check logical by offset.
|
||||
LogicalPosition actualLogicalByOffset = myMapper.offsetToLogicalPosition(data.offset);
|
||||
// We don't to perform the check for the data that points to soft wrap location here. The reason is that it shares offset
|
||||
// with the first document symbol after soft wrap, hence, examination always fails.
|
||||
if (!data.virtualSpace && !data.insideTab && !equals(data.logical, actualLogicalByOffset)) {
|
||||
throw new AssertionError(
|
||||
String.format("Detected unmatched logical position by offset. Expected: '%s', actual: '%s'. Calculation was performed "
|
||||
+ "against offset: '%d' and soft wrap-unaware logical: '%s'",
|
||||
data.logical, actualLogicalByOffset, data.offset, toVisualPositionUnawareLogical(data))
|
||||
);
|
||||
}
|
||||
|
||||
// Check visual by logical.
|
||||
//VisualPosition actualVisualByLogical = myMapper.logicalToVisualPosition(toVisualPositionUnawareLogical(data));
|
||||
//if (!data.virtualSpace && !actualVisualByLogical.equals(data.visual)) {
|
||||
// throw new AssertionError(
|
||||
// String.format("Detected unmatched visual position by logical. Expected: '%s', actual: '%s'. Calculation was performed "
|
||||
// + "against logical position: '%s' and soft wrap-unaware visual: '%s'",
|
||||
// data.visual, actualVisualByLogical, data.logical, toSoftWrapUnawareVisual(data))
|
||||
// );
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equals(LogicalPosition expected, LogicalPosition actual) {
|
||||
return expected.equals(actual) && expected.softWrapLinesBeforeCurrentLogicalLine == actual.softWrapLinesBeforeCurrentLogicalLine
|
||||
&& expected.softWrapLinesOnCurrentLogicalLine == actual.softWrapLinesOnCurrentLogicalLine
|
||||
&& expected.softWrapColumnDiff == actual.softWrapColumnDiff && expected.foldedLines == actual.foldedLines
|
||||
&& expected.foldingColumnDiff == actual.foldingColumnDiff;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssignmentToForLoopParameter"})
|
||||
private void init(final String documentText) {
|
||||
final TestEditorPosition context = new TestEditorPosition();
|
||||
myMockery.checking(new Expectations() {{
|
||||
allowing(myDocument).getCharsSequence(); will(new CustomAction("getCharsSequence()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myChars;
|
||||
}
|
||||
});
|
||||
allowing(myDocument).getText(); will(new CustomAction("getCharsSequence()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myChars.toString();
|
||||
}
|
||||
});
|
||||
allowing(myDocument).getTextLength(); will(new CustomAction("getTextLength()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myChars.length();
|
||||
}
|
||||
});
|
||||
}});
|
||||
for (int i = 0; i < documentText.length(); i++) {
|
||||
if (isSoftWrapStart(documentText, i)) {
|
||||
context.onSoftWrapStart();
|
||||
i += SOFT_WRAP_START_MARKER.length() - 1; // Subtract 1 because 'i' is incremented by 1 on every iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSoftWrapEnd(documentText, i)) {
|
||||
context.onSoftWrapEnd();
|
||||
i += SOFT_WRAP_END_MARKER.length() - 1; // Subtract 1 because 'i' is incremented by 1 on every iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFoldingStart(documentText, i)) {
|
||||
context.onFoldingStart();
|
||||
i += FOLDING_START_MARKER.length() - 1; // Subtract 1 because 'i' is incremented by 1 on every iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFoldingEnd(documentText, i)) {
|
||||
context.onFoldingEnd();
|
||||
i += FOLDING_END_MARKER.length() - 1; // Subtract 1 because 'i' is incremented by 1 on every iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
char c = documentText.charAt(i);
|
||||
context.onNewSymbol(c);
|
||||
}
|
||||
|
||||
myLineRanges.add(new TextRange(context.logicalLineStartOffset, myChars.length()));
|
||||
}
|
||||
|
||||
private static boolean isSoftWrapStart(String document, int index) {
|
||||
return matches(document, index, SOFT_WRAP_START_MARKER);
|
||||
}
|
||||
|
||||
private static boolean isSoftWrapEnd(String document, int index) {
|
||||
return matches(document, index, SOFT_WRAP_END_MARKER);
|
||||
}
|
||||
|
||||
private static boolean isFoldingStart(String document, int index) {
|
||||
return matches(document, index, FOLDING_START_MARKER);
|
||||
}
|
||||
|
||||
private static boolean isFoldingEnd(String document, int index) {
|
||||
return matches(document, index, FOLDING_END_MARKER);
|
||||
}
|
||||
|
||||
private static boolean matches(String document, int index, String pattern) {
|
||||
if (index + pattern.length() > document.length()) {
|
||||
return false;
|
||||
}
|
||||
return pattern.equals(document.substring(index, index + pattern.length()));
|
||||
}
|
||||
|
||||
private static class DataEntry {
|
||||
public final VisualPosition visual;
|
||||
public final LogicalPosition logical;
|
||||
public final int offset;
|
||||
public final boolean foldedSpace;
|
||||
public final boolean virtualSpace;
|
||||
public final boolean insideTab;
|
||||
|
||||
DataEntry(VisualPosition visual, LogicalPosition logical, int offset, boolean foldedSpace) {
|
||||
this(visual, logical, offset, foldedSpace, false, false);
|
||||
}
|
||||
|
||||
DataEntry(VisualPosition visual, LogicalPosition logical, int offset, boolean foldedSpace, boolean virtualSpace, boolean insideTab) {
|
||||
this.visual = visual;
|
||||
this.logical = logical;
|
||||
this.offset = offset;
|
||||
this.foldedSpace = foldedSpace;
|
||||
this.virtualSpace = virtualSpace;
|
||||
this.insideTab = insideTab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "offset: " + offset + ", logical: " + logical + ", visual: " + visual + ", folded: " + foldedSpace
|
||||
+ ", virtual: " + virtualSpace;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestEditorPosition extends EditorPosition {
|
||||
private final StringBuilder mySoftWrapBuffer = new StringBuilder();
|
||||
|
||||
EditorPosition lineStartPosition;
|
||||
|
||||
boolean insideSoftWrap;
|
||||
boolean insideFolding;
|
||||
boolean insideTab;
|
||||
int logicalLineStartOffset;
|
||||
int softWrapStartOffset;
|
||||
int softWrapSymbolsOnCurrentVisualLine;
|
||||
int foldingStartOffset;
|
||||
int foldingStartVisualLine;
|
||||
int foldingStartVisualColumn;
|
||||
|
||||
TestEditorPosition() {
|
||||
super(myEditor);
|
||||
lineStartPosition = clone();
|
||||
}
|
||||
|
||||
public void onSoftWrapStart() {
|
||||
softWrapStartOffset = offset;
|
||||
insideSoftWrap = true;
|
||||
myMapper.onVisualLineStart(lineStartPosition);
|
||||
}
|
||||
|
||||
public void onSoftWrapEnd() {
|
||||
myStorage.storeOrReplace(
|
||||
new SoftWrapImpl(
|
||||
new TextChangeImpl('\n' + mySoftWrapBuffer.toString(), softWrapStartOffset),
|
||||
mySoftWrapBuffer.length() + 1/* for 'after soft wrap' drawing */,
|
||||
(mySoftWrapBuffer.length() * SPACE_SIZE) + SOFT_WRAP_DRAWING_WIDTH
|
||||
));
|
||||
mySoftWrapBuffer.setLength(0);
|
||||
insideSoftWrap = false;
|
||||
x += SOFT_WRAP_DRAWING_WIDTH;
|
||||
}
|
||||
|
||||
public void onFoldingStart() {
|
||||
foldingStartOffset = offset;
|
||||
foldingStartVisualLine = visualLine;
|
||||
foldingStartVisualColumn = visualColumn;
|
||||
insideFolding = true;
|
||||
myMapper.onVisualLineStart(lineStartPosition);
|
||||
}
|
||||
|
||||
public void onFoldingEnd() {
|
||||
visualColumn += 3; // For '...' folding
|
||||
foldingColumnDiff += 3;
|
||||
|
||||
int prevX = x;
|
||||
x += 3 * SPACE_SIZE;
|
||||
insideFolding = false;
|
||||
MockFoldRegion foldRegion = new MockFoldRegion(foldingStartOffset, offset);
|
||||
myFoldRegions.add(foldRegion);
|
||||
myMapper.onCollapsedFoldRegion(foldRegion, myRepresentationHelper.toVisualColumnSymbolsNumber(foldingStartOffset, offset, prevX), foldingStartVisualLine);
|
||||
}
|
||||
|
||||
public void onNewSymbol(char c) {
|
||||
symbol = c;
|
||||
addData();
|
||||
if (insideFolding) {
|
||||
myFoldedOffsets.add(offset);
|
||||
onNonSoftWrapSymbol(c);
|
||||
if (c == '\n') {
|
||||
foldedLines++;
|
||||
offset++;
|
||||
x = 0;
|
||||
softWrapColumnDiff = 0;
|
||||
softWrapLinesBefore += softWrapLinesCurrent;
|
||||
softWrapLinesCurrent = 0;
|
||||
foldingColumnDiff = foldingStartVisualColumn;
|
||||
}
|
||||
else if (c == '\t') {
|
||||
int tabWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x);
|
||||
x += myRepresentationHelper.charWidth(c, x);
|
||||
|
||||
// There is a possible case that single tabulation symbols is shown in more than one visual column at IJ editor.
|
||||
// We store data entry only for the first tab column without 'inside tab' flag then.
|
||||
insideTab = true;
|
||||
for (int i = tabWidthInColumns - 1; i > 0; i--) {
|
||||
logicalColumn++;
|
||||
addData(false);
|
||||
}
|
||||
insideTab = false;
|
||||
|
||||
logicalColumn++;
|
||||
offset++;
|
||||
foldingColumnDiff -= tabWidthInColumns;
|
||||
} else {
|
||||
logicalColumn++;
|
||||
offset++;
|
||||
x += myRepresentationHelper.charWidth(c, x);
|
||||
foldingColumnDiff--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Symbol inside soft wrap.
|
||||
if (insideSoftWrap) {
|
||||
if (c == '\n') {
|
||||
|
||||
myMapper.beforeSoftWrapLineFeed(this);
|
||||
|
||||
// Emulate the situation when the user works with a virtual space after document line end (add such virtual
|
||||
// positions two symbols behind the end).
|
||||
visualColumn++;
|
||||
softWrapColumnDiff++;
|
||||
x += SPACE_SIZE;
|
||||
addData(true);
|
||||
|
||||
visualColumn++;
|
||||
softWrapColumnDiff++;
|
||||
x += SPACE_SIZE;
|
||||
addData(true);
|
||||
|
||||
visualLine++;
|
||||
x = 0;
|
||||
softWrapLinesCurrent++;
|
||||
softWrapColumnDiff = -logicalColumn - foldingColumnDiff;
|
||||
visualColumn = 0;
|
||||
myMapper.afterSoftWrapLineFeed(this);
|
||||
visualColumn = 1; // For the column reserved for soft wrap sign.
|
||||
softWrapColumnDiff++; // For the column reserved for soft wrap sign.
|
||||
softWrapSymbolsOnCurrentVisualLine = 0;
|
||||
}
|
||||
else {
|
||||
mySoftWrapBuffer.append(c);
|
||||
visualColumn++;
|
||||
softWrapColumnDiff++;
|
||||
softWrapSymbolsOnCurrentVisualLine++;
|
||||
x += myRepresentationHelper.charWidth(c, x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Symbol outside soft wrap and folding.
|
||||
onNonSoftWrapSymbol(c);
|
||||
if (c == '\n') {
|
||||
visualLine++;
|
||||
visualColumn = 0;
|
||||
foldingColumnDiff = 0;
|
||||
softWrapColumnDiff = 0;
|
||||
x = 0;
|
||||
softWrapLinesBefore += softWrapLinesCurrent;
|
||||
softWrapLinesCurrent = 0;
|
||||
softWrapSymbolsOnCurrentVisualLine = 0;
|
||||
foldingColumnDiff = 0;
|
||||
offset++;
|
||||
lineStartPosition.from(this);
|
||||
}
|
||||
else if (c == '\t') {
|
||||
myMapper.onVisualLineStart(lineStartPosition);
|
||||
symbolWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x);
|
||||
myMapper.onTabulation(this, symbolWidthInColumns);
|
||||
int oldX = x;
|
||||
x += myRepresentationHelper.charWidth(c, x);
|
||||
symbolWidthInPixels = x - oldX;
|
||||
|
||||
// There is a possible case that single tabulation symbols is shown in more than one visual column at IJ editor.
|
||||
// We store data entry only for the first tab column without 'inside tab' flag then.
|
||||
insideTab = true;
|
||||
for (int i = symbolWidthInColumns - 1; i > 0; i--) {
|
||||
visualColumn++;
|
||||
logicalColumn++;
|
||||
addData(false);
|
||||
}
|
||||
insideTab = false;
|
||||
|
||||
visualColumn++;
|
||||
logicalColumn++;
|
||||
offset++;
|
||||
}
|
||||
else {
|
||||
visualColumn++;
|
||||
logicalColumn++;
|
||||
offset++;
|
||||
x += myRepresentationHelper.charWidth(c, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void onNonSoftWrapSymbol(char c) {
|
||||
myChars.append(c);
|
||||
if (c == '\n') {
|
||||
myLineRanges.add(new TextRange(logicalLineStartOffset, offset));
|
||||
|
||||
// Emulate the situation when the user works with a virtual space after document line end (add such virtual
|
||||
// positions two symbols behind the end).
|
||||
if (!insideFolding) {
|
||||
myMapper.onVisualLineEnd(this);
|
||||
visualColumn++;
|
||||
logicalColumn++;
|
||||
addData(true);
|
||||
visualColumn++;
|
||||
logicalColumn++;
|
||||
addData(true);
|
||||
}
|
||||
|
||||
logicalLineStartOffset = offset + 1;
|
||||
logicalLine++;
|
||||
logicalColumn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void addData() {
|
||||
addData(insideSoftWrap);
|
||||
}
|
||||
|
||||
private void addData(boolean virtualSpace) {
|
||||
myExpectedData.add(new DataEntry(
|
||||
buildVisualPosition(), buildLogicalPosition(), offset, insideFolding && offset != foldingStartOffset, virtualSpace, insideTab
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.CompositeDataProvider;
|
||||
import com.intellij.openapi.editor.impl.softwrap.mapping.DataProvider;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 08/31/2010
|
||||
*/
|
||||
public class CompositeDataProviderTest {
|
||||
|
||||
private enum DataType implements Comparable<DataType> { ONE, TWO }
|
||||
|
||||
private CompositeDataProvider myProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
myProvider = new CompositeDataProvider();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unequalNumberOfDataEntries() {
|
||||
myProvider = new CompositeDataProvider(
|
||||
new TestDataProvider<>(DataType.ONE, 1, 3, 5, 7),
|
||||
new TestDataProvider<>(DataType.TWO, 2, 4)
|
||||
);
|
||||
|
||||
assertTrue(myProvider.hasData());
|
||||
Pair<DataType, Object> data = myProvider.getData();
|
||||
assertSame(data.first, DataType.ONE);
|
||||
assertSame(1, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.hasData());
|
||||
assertTrue(myProvider.next());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.TWO);
|
||||
assertSame(2, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.hasData());
|
||||
assertTrue(myProvider.next());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.ONE);
|
||||
assertSame(3, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.hasData());
|
||||
assertTrue(myProvider.next());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.TWO);
|
||||
assertSame(4, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.hasData());
|
||||
assertTrue(myProvider.next());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.ONE);
|
||||
assertSame(5, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.hasData());
|
||||
assertTrue(myProvider.next());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.ONE);
|
||||
assertSame(7, myProvider.getSortingKey());
|
||||
|
||||
assertFalse(myProvider.next());
|
||||
assertFalse(myProvider.hasData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyProviders() {
|
||||
myProvider = new CompositeDataProvider(
|
||||
new TestDataProvider<>(DataType.ONE),
|
||||
new TestDataProvider<>(DataType.TWO)
|
||||
);
|
||||
|
||||
assertFalse(myProvider.hasData());
|
||||
assertFalse(myProvider.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyProvidersAfterAdvance() {
|
||||
myProvider = new CompositeDataProvider(
|
||||
new TestDataProvider<>(DataType.ONE, 1, 3, 5),
|
||||
new TestDataProvider<>(DataType.TWO, 2, 4)
|
||||
);
|
||||
|
||||
myProvider.advance(4);
|
||||
assertTrue(myProvider.hasData());
|
||||
Pair<DataType, Object> data = myProvider.getData();
|
||||
assertSame(data.first, DataType.TWO);
|
||||
assertSame(4, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.next());
|
||||
assertTrue(myProvider.hasData());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.ONE);
|
||||
assertSame(5, myProvider.getSortingKey());
|
||||
|
||||
assertFalse(myProvider.next());
|
||||
assertFalse(myProvider.hasData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleAdvances() {
|
||||
myProvider = new CompositeDataProvider(
|
||||
new TestDataProvider<>(DataType.ONE, 1, 3, 5),
|
||||
new TestDataProvider<>(DataType.TWO, 2, 4)
|
||||
);
|
||||
|
||||
myProvider.advance(2);
|
||||
assertTrue(myProvider.hasData());
|
||||
Pair<DataType, Object> data = myProvider.getData();
|
||||
assertSame(data.first, DataType.TWO);
|
||||
assertSame(2, myProvider.getSortingKey());
|
||||
|
||||
myProvider.advance(4);
|
||||
assertTrue(myProvider.hasData());
|
||||
data = myProvider.getData();
|
||||
assertSame(data.first, DataType.TWO);
|
||||
assertSame(4, myProvider.getSortingKey());
|
||||
|
||||
myProvider.advance(6);
|
||||
|
||||
assertFalse(myProvider.next());
|
||||
assertFalse(myProvider.hasData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonEmptyProvidersAfterAdvance() {
|
||||
myProvider = new CompositeDataProvider(
|
||||
new TestDataProvider<>(DataType.ONE, 1, 3, 5),
|
||||
new TestDataProvider<>(DataType.TWO, 2, 4)
|
||||
);
|
||||
|
||||
myProvider.advance(6);
|
||||
assertFalse(myProvider.hasData());
|
||||
assertFalse(myProvider.next());
|
||||
}
|
||||
|
||||
private static class TestDataProvider<K extends Comparable<? super K>> implements DataProvider<K, Object> {
|
||||
|
||||
private final K myKey;
|
||||
private final int[] mySortingKeys;
|
||||
private int myIndex;
|
||||
|
||||
private TestDataProvider(@NotNull K key, @NotNull int ... sortingKeys) {
|
||||
myKey = key;
|
||||
mySortingKeys = sortingKeys;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<K, Object> getData() {
|
||||
if (myIndex < mySortingKeys.length) {
|
||||
return new Pair<>(myKey, new Object());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
return ++myIndex < mySortingKeys.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance(int sortingKey) {
|
||||
while (myIndex < mySortingKeys.length && mySortingKeys[myIndex] < sortingKey) {
|
||||
myIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSortingKey() {
|
||||
return mySortingKeys[myIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.mock.MockFoldRegion;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 12/02/2010
|
||||
*/
|
||||
public class FoldingDataProviderTest {
|
||||
|
||||
FoldingDataProvider myProvider;
|
||||
|
||||
@Test
|
||||
public void advanceToExactRegionStartOffset() {
|
||||
init(new MockFoldRegion(1, 3), new MockFoldRegion(5, 10), new MockFoldRegion(15, 20));
|
||||
myProvider.advance(5);
|
||||
assertEquals(5, myProvider.getSortingKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidRegionsAreIgnored() {
|
||||
init(new MockFoldRegion(1, 3), new MockFoldRegion(5, 6, false), new MockFoldRegion(7, 8), new MockFoldRegion(10, 11, false));
|
||||
assertEquals(1, myProvider.getSortingKey());
|
||||
|
||||
assertTrue(myProvider.next());
|
||||
assertEquals(7, myProvider.getSortingKey());
|
||||
|
||||
assertFalse(myProvider.next());
|
||||
assertNull(myProvider.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidRegionsAreIgnoredDuringAdvancing() {
|
||||
init(new MockFoldRegion(1, 3), new MockFoldRegion(5, 6, false), new MockFoldRegion(7, 8), new MockFoldRegion(10, 11, false));
|
||||
assertEquals(1, myProvider.getSortingKey());
|
||||
|
||||
myProvider.advance(5);
|
||||
assertEquals(7, myProvider.getSortingKey());
|
||||
|
||||
assertFalse(myProvider.next());
|
||||
assertNull(myProvider.getData());
|
||||
}
|
||||
|
||||
private void init(FoldRegion ... regions) {
|
||||
myProvider = new FoldingDataProvider(regions);
|
||||
}
|
||||
}
|
||||
+2
-19
@@ -25,7 +25,6 @@ import com.intellij.openapi.editor.impl.AbstractEditorTest;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.testFramework.EditorTestUtil;
|
||||
import com.intellij.testFramework.TestFileType;
|
||||
@@ -1007,24 +1006,8 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorT
|
||||
final String text = "12345678 abcdefgh";
|
||||
init(15, 10, text);
|
||||
myEditor.getCaretModel().moveToOffset(text.length());
|
||||
final Ref<Boolean> fail = new Ref<>(true);
|
||||
SoftWrapApplianceManager applianceManager = ((SoftWrapModelImpl)myEditor.getSoftWrapModel()).getApplianceManager();
|
||||
SoftWrapAwareDocumentParsingListener listener = new SoftWrapAwareDocumentParsingListenerAdapter() {
|
||||
@Override
|
||||
public void beforeSoftWrapLineFeed(@NotNull EditorPosition position) {
|
||||
if (position.x == text.indexOf("a") * 10) {
|
||||
fail.set(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
applianceManager.addListener(listener);
|
||||
try {
|
||||
backspace();
|
||||
}
|
||||
finally {
|
||||
applianceManager.removeListener(listener);
|
||||
}
|
||||
assertFalse(fail.get());
|
||||
backspace();
|
||||
verifySoftWrapPositions(9);
|
||||
}
|
||||
|
||||
public void testCaretInsideFoldRegionOnCollapse() throws IOException {
|
||||
|
||||
@@ -665,9 +665,6 @@ html.prefer.short.notation.of.boolean.attributes=true
|
||||
editor.disable.rtl=false
|
||||
editor.disable.rtl.description=Disables RTL support in editor (all characters are displayed in LTR order)
|
||||
|
||||
editor.new.rendering=true
|
||||
editor.new.rendering.description=Enables new editor rendering code, with support for RTL.
|
||||
|
||||
editor.zero.latency.rendering=true
|
||||
editor.zero.latency.rendering.description=Enables zero-latency rendering mode in editor
|
||||
|
||||
|
||||
Reference in New Issue
Block a user