blob: 399406f2db32408d1a8876f6702356873a51daad [file] [log] [blame]
/*
* 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.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 Editor 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.onNewLine(true);
}
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.onNewLine(true);
setInitialPosition(position);
return;
}
int i = MappingUtil.getCacheEntryIndexForOffset(targetOffset, myEditor.getDocument(), cache);
CacheEntry cacheEntry = null;
if (i >= 0) {
CacheEntry candidate = cache.get(i);
// There is a possible case that target offset points to the start of soft-wrap introduced visual line. We perform eager
// match then.
if (candidate.endOffset == targetOffset && i < cache.size() - 1 && cache.get(i + 1).startOffset == targetOffset) {
EditorPosition position = cache.get(i + 1).buildStartLinePosition();
SoftWrap softWrap = myStorage.getSoftWrap(targetOffset);
if (softWrap != null) {
position.visualColumn = softWrap.getIndentInColumns();
position.softWrapColumnDiff += softWrap.getIndentInColumns();
setEagerMatch(position.buildLogicalPosition());
}
}
else 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;
// Process use-case when target offset points to 'after soft wrap' position.
//SoftWrap softWrap = myStorage.getSoftWrap(offset);
//if (softWrap != null && offset < getAnchorCacheEntry().endOffset) {
// position.visualColumn = softWrap.getIndentInColumns();
// position.softWrapColumnDiff = position.visualColumn - position.logicalColumn;
// return position.buildLogicalPosition();
//}
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(
document.getCharsSequence(), foldRegion.getStartOffset(), myTargetOffset, position.x
);
}
else {
// Target offset is located on a different line with folding start.
position.logicalColumn = SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor).toVisualColumnSymbolsNumber(
document.getCharsSequence(), 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();
}
}