speed up editor opening - avoid unneeded/repetitive calculations

This commit is contained in:
Dmitry Batrak
2015-11-10 19:50:44 +03:00
parent 1b03cec221
commit 70bcea7250
9 changed files with 298 additions and 111 deletions
@@ -73,7 +73,7 @@ class EditorCoordinateMapper {
int visualLine = offsetToVisualLine(offset, beforeSoftWrap);
int maxVisualColumn = 0;
int maxLogicalColumn = 0;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) {
if (!pos.leansForward && offset == fragment.getVisualLineStartOffset()) {
return new VisualPosition(visualLine, fragment.getStartVisualColumn());
}
@@ -125,7 +125,7 @@ class EditorCoordinateMapper {
int maxVisualColumn = 0;
int maxLogicalColumn = 0;
int maxOffset = offset;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) {
int minColumn = fragment.getStartVisualColumn();
int maxColumn = fragment.getEndVisualColumn();
if (column < minColumn || column == minColumn && !pos.leansRight) {
@@ -240,7 +240,7 @@ class EditorCoordinateMapper {
if (visualLine < myView.getEditor().getVisibleLineCount()) {
int visualLineStartOffset = visualLineToOffset(visualLine);
int maxOffset = 0;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) {
if (p.x <= fragment.getStartX()) {
int markerWidth = myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
float indent = fragment.getStartX() - markerWidth;
@@ -286,7 +286,7 @@ class EditorCoordinateMapper {
if (visualLine < myView.getEditor().getVisibleLineCount()) {
int visualLineStartOffset = visualLineToOffset(visualLine);
int maxOffset = 0;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) {
if (column < fragment.getStartVisualColumn()) {
break;
}
@@ -317,7 +317,7 @@ class EditorCoordinateMapper {
float x = getStartX(logicalLine);
if (myDocument.getTextLength() > 0) {
boolean firstFragment = true;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) {
if (firstFragment && offset == visualLineStartOffset && !leanTowardsLargerOffsets) {
x = fragment.getStartX();
break;
@@ -589,7 +589,7 @@ class EditorPainter implements TextDrawingCallback {
TFloatArrayList trailingRanges = adjustedLogicalRangeToVisualRanges(
myView.visualPositionToOffset(new VisualPosition(endPosition.line, 0)), endOffset);
if (!leadingRanges.isEmpty() && !trailingRanges.isEmpty()) {
int maxWidth = Math.max(myView.getMaxWidthInLineRange(startPosition.line, endPosition.line - 1, null) - 1,
int maxWidth = Math.max(myView.getMaxWidthInLineRange(startPosition.line, endPosition.line - 1) - 1,
(int)trailingRanges.get(trailingRanges.size() - 1));
boolean containsInnerLines = endPosition.line > startPosition.line + 1;
int leadingTopY = myView.visualLineToY(startPosition.line);
@@ -695,7 +695,7 @@ class EditorPainter implements TextDrawingCallback {
result.add(0);
}
else {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset, false, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset, false)) {
int minOffset = fragment.getMinOffset();
int maxOffset = fragment.getMaxOffset();
if (startOffset == endOffset) {
@@ -777,7 +777,7 @@ class EditorPainter implements TextDrawingCallback {
int targetVisualColumn = caret.getVisualPosition().column;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView,
caret.getVisualLineStart(),
false, null)) {
false)) {
int startVisualColumn = fragment.getStartVisualColumn();
int endVisualColumn = fragment.getEndVisualColumn();
if (startVisualColumn < targetVisualColumn && endVisualColumn > targetVisualColumn ||
@@ -815,7 +815,7 @@ class EditorPainter implements TextDrawingCallback {
int prevEndOffset = -1;
boolean firstFragment = true;
int maxColumn = 0;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false, null)) {
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) {
int fragmentStartOffset = fragment.getStartOffset();
int start = fragmentStartOffset;
int end = fragment.getEndOffset();
@@ -26,6 +26,7 @@ import com.intellij.openapi.editor.ex.FoldingListener;
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
import com.intellij.openapi.editor.impl.EditorDocumentPriorities;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
import com.intellij.openapi.editor.impl.softwrap.mapping.IncrementalCacheUpdateEvent;
import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapAwareDocumentParsingListenerAdapter;
import com.intellij.openapi.project.Project;
@@ -34,6 +35,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
@@ -195,26 +197,56 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold
}
private int calculatePreferredWidth() {
int lineCount = myLineWidths.size();
assert myLineWidths.size() == myEditor.getVisibleLineCount();
VisualLinesIterator iterator = new VisualLinesIterator(myView, 0);
int maxWidth = 0;
for (int i = 0; i < lineCount; i++) {
int width = myLineWidths.get(i);
while (!iterator.atEnd()) {
int visualLine = iterator.getVisualLine();
int width = myLineWidths.get(visualLine);
if (width == UNKNOWN_WIDTH) {
final Ref<Boolean> approximateValue = new Ref<Boolean>(Boolean.FALSE);
width = myView.getMaxWidthInLineRange(i, i, new Runnable() {
width = getVisualLineWidth(iterator, new Runnable() {
@Override
public void run() {
approximateValue.set(Boolean.TRUE);
}
});
if (approximateValue.get()) width = -width;
myLineWidths.set(i, width);
myLineWidths.set(visualLine, width);
}
maxWidth = Math.max(maxWidth, Math.abs(width));
iterator.advance();
}
return maxWidth;
}
int getVisualLineWidth(VisualLinesIterator visualLinesIterator, @Nullable Runnable quickEvaluationListener) {
assert !visualLinesIterator.atEnd();
int visualLine = visualLinesIterator.getVisualLine();
FoldRegion[] topLevelRegions = myEditor.getFoldingModel().fetchTopLevel();
if (quickEvaluationListener != null &&
(topLevelRegions == null || topLevelRegions.length == 0) && myEditor.getSoftWrapModel().getRegisteredSoftWraps().isEmpty() &&
!myView.getTextLayoutCache().hasCachedLayoutFor(visualLine)) {
// fast path - speeds up editor opening
quickEvaluationListener.run();
return myView.getLogicalPositionCache().offsetToLogicalColumn(visualLine,
myDocument.getLineEndOffset(visualLine) -
myDocument.getLineStartOffset(visualLine)) *
myView.getMaxCharWidth();
}
float x = 0;
int maxOffset = visualLinesIterator.getVisualLineStartOffset();
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLinesIterator,
quickEvaluationListener)) {
x = fragment.getEndX();
maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
}
if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) {
x += myEditor.getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
}
return (int)x;
}
void reset() {
assert !myDocument.isInBulkUpdate();
doInvalidateRange(0, myDocument.getTextLength());
@@ -28,13 +28,11 @@ import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.impl.FontInfo;
import com.intellij.openapi.editor.impl.TextDrawingCallback;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.font.FontRenderContext;
@@ -173,6 +171,12 @@ public class EditorView implements TextDrawingCallback, Disposable {
return myMapper.offsetToVisualLine(offset, beforeSoftWrap);
}
public int visualLineToOffset(int visualLine) {
assertIsDispatchThread();
myEditor.getSoftWrapModel().prepareToMapping();
return myMapper.visualLineToOffset(visualLine);
}
public int visualLineStartOffset(int visualLine) {
assertIsDispatchThread();
myEditor.getSoftWrapModel().prepareToMapping();
@@ -240,52 +244,31 @@ public class EditorView implements TextDrawingCallback, Disposable {
public Dimension getPreferredSize() {
assertIsDispatchThread();
myEditor.getSoftWrapModel().prepareToMapping();
return mySizeManager.getPreferredSize();
}
public int getMaxWidthInRange(int startOffset, int endOffset) {
assertIsDispatchThread();
return getMaxWidthInLineRange(offsetToVisualLine(startOffset, false), offsetToVisualLine(endOffset, true), null);
return getMaxWidthInLineRange(offsetToVisualLine(startOffset, false), offsetToVisualLine(endOffset, true));
}
/**
* If <code>quickEvaluationListener</code> is provided, quick approximate size evaluation becomes enabled, listener will be invoked
* if approximation will in fact be used during width calculation.
*/
int getMaxWidthInLineRange(int startVisualLine, int endVisualLine, @Nullable Runnable quickEvaluationListener) {
int getMaxWidthInLineRange(int startVisualLine, int endVisualLine) {
myEditor.getSoftWrapModel().prepareToMapping();
int maxWidth = 0;
endVisualLine = Math.min(endVisualLine, myEditor.getVisibleLineCount() - 1);
for (int i = startVisualLine; i <= endVisualLine; i++) {
int width = getVisualLineWidth(i, quickEvaluationListener);
VisualLinesIterator iterator = new VisualLinesIterator(this, startVisualLine);
while (!iterator.atEnd() && iterator.getVisualLine() <= endVisualLine) {
int width = mySizeManager.getVisualLineWidth(iterator, null);
maxWidth = Math.max(maxWidth, width);
iterator.advance();
}
return maxWidth;
}
private int getVisualLineWidth(int visualLine, @Nullable Runnable quickEvaluationListener) {
FoldRegion[] topLevelRegions = myEditor.getFoldingModel().fetchTopLevel();
if (quickEvaluationListener != null &&
(topLevelRegions == null || topLevelRegions.length == 0) && myEditor.getSoftWrapModel().getRegisteredSoftWraps().isEmpty() &&
!myTextLayoutCache.hasCachedLayoutFor(visualLine)) {
// fast path - speeds up editor opening
quickEvaluationListener.run();
return myMapper.offsetToLogicalPosition(myDocument.getLineEndOffset(visualLine)).column * getMaxCharWidth();
}
int startOffset = myMapper.visualLineToOffset(visualLine);
float x = 0;
int maxOffset = 0;
for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(this, startOffset, false,
quickEvaluationListener)) {
x = fragment.getEndX();
maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
}
if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) {
x += myEditor.getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
}
return (int)x;
}
public void reinitSettings() {
assertIsDispatchThread();
myPlainSpaceWidth = -1;
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.editor.impl.view;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.bidi.BidiRegionsSeparator;
import com.intellij.openapi.editor.bidi.LanguageBidiRegionsSeparator;
import com.intellij.openapi.editor.colors.FontPreferences;
@@ -46,8 +47,8 @@ class LineLayout {
/**
* Creates a layout for a fragment of text from editor.
*/
LineLayout(@NotNull EditorView view, int startOffset, int endOffset, boolean skipBidiLayout) {
this(createFragments(view, startOffset, endOffset, skipBidiLayout), false);
LineLayout(@NotNull EditorView view, int line, boolean skipBidiLayout) {
this(createFragments(view, line, skipBidiLayout), false);
}
/**
@@ -56,7 +57,7 @@ class LineLayout {
LineLayout(@NotNull EditorView view, @NotNull CharSequence text, @JdkConstants.FontStyle int fontStyle) {
this(createFragments(view, text, fontStyle), true);
}
private LineLayout(@NotNull List<BidiRun> runs, boolean calculateWidth) {
myBidiRunsInLogicalOrder = runs.toArray(new BidiRun[runs.size()]);
myBidiRunsInVisualOrder = myBidiRunsInLogicalOrder.clone();
@@ -65,7 +66,7 @@ class LineLayout {
myWidth = calculateWidth ? calculateWidth() : -1;
}
// runs are supposed to be in logical order initially
private static void reorderRunsVisually(BidiRun[] bidiRuns) {
if (bidiRuns.length > 1) {
@@ -82,11 +83,14 @@ class LineLayout {
return Bidi.requiresBidi(chars, 0, chars.length);
}
private static List<BidiRun> createFragments(@NotNull EditorView view, int lineStartOffset, int lineEndOffset, boolean skipBidiLayout) {
private static List<BidiRun> createFragments(@NotNull EditorView view, int line, boolean skipBidiLayout) {
EditorImpl editor = view.getEditor();
Document document = editor.getDocument();
int lineStartOffset = document.getLineStartOffset(line);
int lineEndOffset = document.getLineEndOffset(line);
if (lineEndOffset <= lineStartOffset) return Collections.emptyList();
if (skipBidiLayout) return Collections.singletonList(new BidiRun(lineEndOffset - lineStartOffset));
EditorImpl editor = view.getEditor();
CharSequence text = editor.getDocument().getImmutableCharSequence().subSequence(lineStartOffset, lineEndOffset);
CharSequence text = document.getImmutableCharSequence().subSequence(lineStartOffset, lineEndOffset);
char[] chars = CharArrayUtil.fromSequence(text);
return createRuns(editor, chars, lineStartOffset);
}
@@ -240,8 +244,8 @@ class LineLayout {
* If <code>quickEvaluationListener</code> is provided, quick approximate iteration becomes enabled, listener will be invoked
* if approximation will in fact be used during width calculation.
*/
Iterable<VisualFragment> getFragmentsInVisualOrder(@NotNull final EditorView view,
final int lineStartOffset,
Iterator<VisualFragment> getFragmentsInVisualOrder(@NotNull final EditorView view,
final int line,
final float startX,
final int startVisualColumn,
final int startOffset,
@@ -257,18 +261,13 @@ class LineLayout {
for (BidiRun run : myBidiRunsInLogicalOrder) {
if (run.endOffset <= startOffset) continue;
if (run.startOffset >= endOffset) break;
runList.add(run.subRun(view, lineStartOffset, startOffset, endOffset, quickEvaluationListener));
runList.add(run.subRun(view, line, startOffset, endOffset, quickEvaluationListener));
}
runs = runList.toArray(new BidiRun[runList.size()]);
reorderRunsVisually(runs);
}
final int startLogicalColumn = view.offsetToLogicalPosition(lineStartOffset + startOffset).column;
return new Iterable<VisualFragment>() {
@Override
public Iterator<VisualFragment> iterator() {
return new VisualOrderIterator(view, lineStartOffset, startX, startVisualColumn, startLogicalColumn, startOffset, runs);
}
};
final int startLogicalColumn = view.getLogicalPositionCache().offsetToLogicalColumn(line, startOffset);
return new VisualOrderIterator(view, line, startX, startVisualColumn, startLogicalColumn, startOffset, runs);
}
boolean isLtr() {
@@ -348,7 +347,7 @@ class LineLayout {
return chunks;
}
private BidiRun subRun(@NotNull EditorView view, int lineStartOffset, int targetStartOffset, int targetEndOffset,
private BidiRun subRun(@NotNull EditorView view, int line, int targetStartOffset, int targetEndOffset,
@Nullable Runnable quickEvaluationListener) {
assert targetStartOffset < endOffset;
assert targetEndOffset > startOffset;
@@ -359,7 +358,7 @@ class LineLayout {
for (Chunk chunk : getChunks()) {
if (chunk.endOffset <= start) continue;
if (chunk.startOffset >= end) break;
subChunks.add(chunk.subChunk(view, this, lineStartOffset, start, end, quickEvaluationListener));
subChunks.add(chunk.subChunk(view, this, line, start, end, quickEvaluationListener));
}
subRun.chunks = subChunks.toArray(new Chunk[subChunks.size()]);
return subRun;
@@ -376,11 +375,12 @@ class LineLayout {
this.endOffset = endOffset;
}
private void ensureLayout(@NotNull EditorView view, BidiRun run, int lineStartOffset) {
private void ensureLayout(@NotNull EditorView view, BidiRun run, int line) {
if (isReal()) {
view.getTextLayoutCache().onChunkAccess(this);
}
if (!fragments.isEmpty()) return;
int lineStartOffset = view.getEditor().getDocument().getLineStartOffset(line);
int start = lineStartOffset + startOffset;
int end = lineStartOffset + endOffset;
IterationState it = new IterationState(view.getEditor(), start, end, false, false, false, false);
@@ -395,7 +395,7 @@ class LineLayout {
assert !fragments.isEmpty();
}
private Chunk subChunk(EditorView view, BidiRun run, int lineStartOffset, int targetStartOffset, int targetEndOffset,
private Chunk subChunk(EditorView view, BidiRun run, int line, int targetStartOffset, int targetEndOffset,
@Nullable Runnable quickEvaluationListener) {
assert targetStartOffset < endOffset;
assert targetEndOffset > startOffset;
@@ -403,12 +403,12 @@ class LineLayout {
int end = Math.min(endOffset, targetEndOffset);
if (quickEvaluationListener != null && fragments.isEmpty()) {
quickEvaluationListener.run();
return new ApproximationChunk(view, lineStartOffset, start, end);
return new ApproximationChunk(view, line, start, end);
}
if (start == startOffset && end == this.endOffset) {
return this;
}
ensureLayout(view, run, lineStartOffset);
ensureLayout(view, run, line);
Chunk chunk = new Chunk(start, end);
int offset = startOffset;
for (LineFragment fragment : fragments) {
@@ -432,10 +432,10 @@ class LineLayout {
}
private static class ApproximationChunk extends Chunk {
private ApproximationChunk(@NotNull EditorView view, int lineStartOffset, int start, int end) {
private ApproximationChunk(@NotNull EditorView view, int line, int start, int end) {
super(start, end);
int startColumn = view.offsetToLogicalPosition(lineStartOffset + start).column;
int endColumn = view.offsetToLogicalPosition(lineStartOffset + end).column;
int startColumn = view.getLogicalPositionCache().offsetToLogicalColumn(line, start);
int endColumn = view.getLogicalPositionCache().offsetToLogicalColumn(line, end);
fragments.add(new ApproximationFragment(end - start, endColumn - startColumn, view.getMaxCharWidth()));
}
@@ -447,7 +447,7 @@ class LineLayout {
private static class VisualOrderIterator implements Iterator<VisualFragment> {
private final EditorView myView;
private final int myLineStartOffset;
private final int myLine;
private final BidiRun[] myRuns;
private int myRunIndex = 0;
private int myChunkIndex = 0;
@@ -455,10 +455,10 @@ class LineLayout {
private int myOffsetInsideRun = 0;
private VisualFragment myFragment = new VisualFragment();
private VisualOrderIterator(EditorView view, int lineStartOffset,
private VisualOrderIterator(EditorView view, int line,
float startX, int startVisualColumn, int startLogicalColumn, int startOffset, BidiRun[] runsInVisualOrder) {
myView = view;
myLineStartOffset = lineStartOffset;
myLine = line;
myRuns = runsInVisualOrder;
myFragment.startX = startX;
myFragment.startVisualColumn = startVisualColumn;
@@ -474,7 +474,7 @@ class LineLayout {
if (myChunkIndex >= chunks.length) return false;
Chunk chunk = chunks[run.isRtl() ? chunks.length - 1 - myChunkIndex : myChunkIndex];
if (myView != null) {
chunk.ensureLayout(myView, run, myLineStartOffset);
chunk.ensureLayout(myView, run, myLine);
}
return myFragmentIndex < chunk.fragments.size();
}
@@ -83,6 +83,12 @@ class LogicalPositionCache implements PrioritizedDocumentListener, Disposable {
LineData lineData = getLineInfo(line);
return new LogicalPosition(line, lineData.offsetToLogicalColumn(myDocument, line, myTabSize, offset));
}
synchronized int offsetToLogicalColumn(int line, int intraLineOffset) {
if (line < 0 || line >= myDocument.getLineCount()) return 0;
LineData lineData = getLineInfo(line);
return lineData.offsetToLogicalColumn(myDocument, line, myTabSize, myDocument.getLineStartOffset(line) + intraLineOffset);
}
synchronized int logicalPositionToOffset(@NotNull LogicalPosition pos) {
int line = pos.line;
@@ -127,7 +127,7 @@ class TextLayoutCache implements PrioritizedDocumentListener, Disposable {
checkDisposed();
LineLayout result = myLines.get(line);
if (result == null || result == myBidiNotRequiredMarker) {
result = createLineLayout(line, result == myBidiNotRequiredMarker);
result = new LineLayout(myView, line, result == myBidiNotRequiredMarker);
myLines.set(line, result);
}
return result;
@@ -138,13 +138,6 @@ class TextLayoutCache implements PrioritizedDocumentListener, Disposable {
return layout != null && layout != myBidiNotRequiredMarker;
}
@NotNull
private LineLayout createLineLayout(int line, boolean skipBidiLayout) {
int lineStart = myDocument.getLineStartOffset(line);
int lineEnd = myDocument.getLineEndOffset(line);
return new LineLayout(myView, lineStart, lineEnd, skipBidiLayout);
}
private int getChunkCacheSizeLimit() {
return myView.getEditor().getContentComponent().isShowing() ? MAX_CHUNKS_IN_ACTIVE_EDITOR : MAX_CHUNKS_IN_INACTIVE_EDITOR;
}
@@ -22,6 +22,7 @@ import com.intellij.openapi.editor.ex.FoldingModelEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
@@ -35,26 +36,35 @@ import java.util.NoSuchElementException;
*/
class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterator.Fragment> {
/**
* If <code>quickEvaluationListener</code> is provided, quick approximate iteration mode becomes enabled, listener will be invoked
* if approximation will in fact be used during width calculation.
*/
static Iterable<Fragment> create(final EditorView view, final int offset, final boolean beforeSoftWrap,
@Nullable final Runnable quickEvaluationListener) {
static Iterable<Fragment> create(final EditorView view, final int offset, final boolean beforeSoftWrap) {
return new Iterable<Fragment>() {
@Override
public Iterator<Fragment> iterator() {
return new VisualLineFragmentsIterator(view, offset, beforeSoftWrap, quickEvaluationListener);
return new VisualLineFragmentsIterator(view, offset, beforeSoftWrap, null);
}
};
}
private final EditorView myView;
private final Document myDocument;
private final FoldRegion[] myRegions;
private final Fragment myFragment = new Fragment();
private final int myVisualLineStartOffset;
private final Runnable myQuickEvaluationListener;
/**
* If <code>quickEvaluationListener</code> is provided, quick approximate iteration mode becomes enabled, listener will be invoked
* if approximation will in fact be used during width calculation.
*/
static Iterable<Fragment> create(final EditorView view, @NotNull final VisualLinesIterator visualLinesIterator,
@Nullable final Runnable quickEvaluationListener) {
return new Iterable<Fragment>() {
@Override
public Iterator<Fragment> iterator() {
return new VisualLineFragmentsIterator(view, visualLinesIterator, quickEvaluationListener);
}
};
}
private EditorView myView;
private Document myDocument;
private FoldRegion[] myRegions;
private Fragment myFragment = new Fragment();
private int myVisualLineStartOffset;
private Runnable myQuickEvaluationListener;
private int mySegmentStartOffset;
private int mySegmentEndOffset;
@@ -69,15 +79,9 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
private int myNextWrapOffset;
private VisualLineFragmentsIterator(EditorView view, int offset, boolean beforeSoftWrap, @Nullable Runnable quickEvaluationListener) {
myQuickEvaluationListener = quickEvaluationListener;
myView = view;
EditorImpl editor = view.getEditor();
myDocument = editor.getDocument();
FoldingModelEx foldingModel = editor.getFoldingModel();
FoldRegion[] regions = foldingModel.fetchTopLevel();
myRegions = regions == null ? FoldRegion.EMPTY_ARRAY : regions;
int visualLineStartOffset = EditorUtil.getNotFoldedLineStartOffset(editor, offset);
SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel();
List<? extends SoftWrap> softWraps = softWrapModel.getRegisteredSoftWraps();
int currentOrPrevWrapIndex = softWrapModel.getSoftWrapIndex(offset);
@@ -89,16 +93,51 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
}
SoftWrap currentOrPrevWrap = currentOrPrevWrapIndex < 0 || currentOrPrevWrapIndex >= softWraps.size() ? null :
softWraps.get(currentOrPrevWrapIndex);
SoftWrap followingWrap = (currentOrPrevWrapIndex + 1) >= softWraps.size() ? null : softWraps.get(currentOrPrevWrapIndex + 1);
if (currentOrPrevWrap != null && currentOrPrevWrap.getStart() > visualLineStartOffset) {
visualLineStartOffset = currentOrPrevWrap.getStart();
}
myVisualLineStartOffset = mySegmentStartOffset = visualLineStartOffset;
int nextFoldingIndex = editor.getFoldingModel().getLastCollapsedRegionBefore(visualLineStartOffset) + 1;
init(view,
visualLineStartOffset,
editor.getDocument().getLineNumber(visualLineStartOffset),
currentOrPrevWrapIndex,
nextFoldingIndex,
quickEvaluationListener);
}
myCurrentFoldRegionIndex = foldingModel.getLastCollapsedRegionBefore(mySegmentStartOffset) + 1;
myCurrentEndLogicalLine = myDocument.getLineNumber(mySegmentStartOffset);
public VisualLineFragmentsIterator(@NotNull EditorView view, @NotNull VisualLinesIterator visualLinesIterator,
@Nullable Runnable quickEvaluationListener) {
assert !visualLinesIterator.atEnd();
init(view,
visualLinesIterator.getVisualLineStartOffset(),
visualLinesIterator.getStartLogicalLine(),
visualLinesIterator.getStartOrPrevWrapIndex(),
visualLinesIterator.getStartFoldingIndex(),
quickEvaluationListener);
}
private void init(EditorView view, int startOffset, int startLogicalLine, int currentOrPrevWrapIndex, int nextFoldingIndex,
@Nullable Runnable quickEvaluationListener) {
myQuickEvaluationListener = quickEvaluationListener;
myView = view;
EditorImpl editor = view.getEditor();
myDocument = editor.getDocument();
FoldingModelEx foldingModel = editor.getFoldingModel();
FoldRegion[] regions = foldingModel.fetchTopLevel();
myRegions = regions == null ? FoldRegion.EMPTY_ARRAY : regions;
SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel();
List<? extends SoftWrap> softWraps = softWrapModel.getRegisteredSoftWraps();
SoftWrap currentOrPrevWrap = currentOrPrevWrapIndex < 0 || currentOrPrevWrapIndex >= softWraps.size() ? null :
softWraps.get(currentOrPrevWrapIndex);
SoftWrap followingWrap = (currentOrPrevWrapIndex + 1) < 0 || (currentOrPrevWrapIndex + 1) >= softWraps.size() ? null :
softWraps.get(currentOrPrevWrapIndex + 1);
myVisualLineStartOffset = mySegmentStartOffset = startOffset;
myCurrentFoldRegionIndex = nextFoldingIndex;
myCurrentEndLogicalLine = startLogicalLine;
if (mySegmentStartOffset == 0) {
myCurrentX = myView.getPrefixTextWidthInPixels();
}
@@ -117,9 +156,9 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
mySegmentEndOffset = Math.min(myNextWrapOffset, Math.min(mySegmentEndOffset, myDocument.getLineEndOffset(line)));
int lineStartOffset = myDocument.getLineStartOffset(line);
myFragmentIterator = myView.getTextLayoutCache().getLineLayout(line).
getFragmentsInVisualOrder(myView, lineStartOffset, myCurrentX, myCurrentVisualColumn,
mySegmentStartOffset - lineStartOffset, mySegmentEndOffset - lineStartOffset,
myQuickEvaluationListener).iterator();
getFragmentsInVisualOrder(myView, line, myCurrentX, myCurrentVisualColumn,
mySegmentStartOffset - lineStartOffset, mySegmentEndOffset - lineStartOffset,
myQuickEvaluationListener);
}
}
@@ -0,0 +1,134 @@
/*
* 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.view;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.editor.SoftWrap;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
import org.jetbrains.annotations.NotNull;
import java.util.List;
class VisualLinesIterator {
private final Document myDocument;
private final FoldRegion[] myFoldRegions;
private final List<? extends SoftWrap> mySoftWraps;
private int myVisualLine;
private int myOffset;
private int myLogicalLine = 1;
private int myFoldRegion;
private int mySoftWrap;
VisualLinesIterator(@NotNull EditorView view, int startVisualLine) {
EditorImpl editor = view.getEditor();
SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel();
myDocument = editor.getDocument();
FoldRegion[] regions = editor.getFoldingModel().fetchTopLevel();
myFoldRegions = regions == null ? FoldRegion.EMPTY_ARRAY : regions;
mySoftWraps = softWrapModel.getRegisteredSoftWraps();
if (startVisualLine < 0 || startVisualLine >= editor.getVisibleLineCount()) {
myOffset = -1;
}
else if (startVisualLine > 0) {
myVisualLine = startVisualLine;
myOffset = startVisualLine >= 0 && startVisualLine < editor.getVisibleLineCount() ? view.visualLineToOffset(startVisualLine) : -1;
myLogicalLine = myDocument.getLineNumber(myOffset) + 1;
mySoftWrap = softWrapModel.getSoftWrapIndex(myOffset) + 1;
if (mySoftWrap <= 0) {
mySoftWrap = -mySoftWrap;
}
myFoldRegion = editor.getFoldingModel().getLastCollapsedRegionBefore(myOffset) + 1;
}
}
boolean atEnd() {
return myOffset == -1;
}
void advance() {
checkEnd();
int nextWrapOffset = getNextSoftWrapOffset();
int nextLineStart = getNextNotFoldedLineStartOffset();
myOffset = Math.min(nextWrapOffset, nextLineStart);
if (myOffset == Integer.MAX_VALUE) {
myOffset = -1;
}
else if (myOffset == nextWrapOffset) {
mySoftWrap++;
}
else {
myLogicalLine++;
}
myVisualLine++;
}
private int getNextSoftWrapOffset() {
return mySoftWrap < mySoftWraps.size() ? mySoftWraps.get(mySoftWrap).getStart() : Integer.MAX_VALUE;
}
private int getNextNotFoldedLineStartOffset() {
while (myLogicalLine < myDocument.getLineCount()) {
int lineStartOffset = myDocument.getLineStartOffset(myLogicalLine);
if (!isCollapsed(lineStartOffset)) return lineStartOffset;
myLogicalLine++;
}
return Integer.MAX_VALUE;
}
private boolean isCollapsed(int offset) {
while (myFoldRegion < myFoldRegions.length) {
FoldRegion foldRegion = myFoldRegions[myFoldRegion];
if (offset <= foldRegion.getStartOffset()) return false;
if (offset <= foldRegion.getEndOffset()) return true;
myFoldRegion++;
}
return false;
}
int getVisualLine() {
checkEnd();
return myVisualLine;
}
int getVisualLineStartOffset() {
checkEnd();
return myOffset;
}
int getStartLogicalLine() {
checkEnd();
return myLogicalLine - 1;
}
int getStartOrPrevWrapIndex() {
checkEnd();
return mySoftWrap - 1;
}
int getStartFoldingIndex() {
checkEnd();
return myFoldRegion;
}
private void checkEnd() {
if (atEnd()) throw new IllegalStateException("Iteration finished");
}
}