diff: allow to create LineOffsets from Document

This commit is contained in:
Aleksey Pivovarov
2017-03-30 19:45:04 +03:00
committed by Aleksey Pivovarov
parent 924b76e03d
commit 6ba9ed4eaf
6 changed files with 160 additions and 77 deletions
@@ -36,6 +36,7 @@ import com.intellij.diff.tools.util.base.HighlightPolicy;
import com.intellij.diff.tools.util.base.IgnorePolicy;
import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
import com.intellij.diff.tools.util.text.LineOffsets;
import com.intellij.diff.tools.util.text.LineOffsetsUtil;
import com.intellij.diff.tools.util.text.MergeInnerDifferences;
import com.intellij.diff.tools.util.text.TextDiffProviderBase;
import com.intellij.diff.util.*;
@@ -406,7 +407,7 @@ public class TextMergeViewer implements MergeTool.MergeViewer {
indicator.checkCanceled();
return ContainerUtil.map(contents, content -> content.getDocument().getImmutableCharSequence());
});
List<LineOffsets> lineOffsets = ContainerUtil.map(sequences, LineOffsets::create);
List<LineOffsets> lineOffsets = ContainerUtil.map(sequences, LineOffsetsUtil::create);
ComparisonManager manager = ComparisonManager.getInstance();
List<MergeLineFragment> lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2),
@@ -15,61 +15,14 @@
*/
package com.intellij.diff.tools.util.text;
import com.intellij.openapi.util.text.StringUtil;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
public interface LineOffsets {
int getLineStart(int line);
public class LineOffsets {
private final int[] myLineEnds;
private final int myTextLength;
int getLineEnd(int line);
private LineOffsets(int[] ends, int length) {
myLineEnds = ends;
myTextLength = length;
}
int getLineNumber(int offset);
public int getLineStart(int line) {
checkLineIndex(line);
if (line == 0) return 0;
return myLineEnds[line - 1] + 1;
}
int getLineCount();
public int getLineEnd(int line) {
checkLineIndex(line);
return myLineEnds[line];
}
public int getLineCount() {
return myLineEnds.length;
}
public int getTextLength() {
return myTextLength;
}
private void checkLineIndex(int index) {
if (index < 0 || index >= getLineCount()) {
throw new IndexOutOfBoundsException("Wrong line: " + index + ". Available lines count: " + getLineCount());
}
}
@NotNull
public static LineOffsets create(@NotNull CharSequence text) {
TIntArrayList ends = new TIntArrayList();
int index = 0;
while (true) {
int lineEnd = StringUtil.indexOf(text, '\n', index);
if (lineEnd != -1) {
ends.add(lineEnd);
index = lineEnd + 1;
}
else {
ends.add(text.length());
break;
}
}
return new LineOffsets(ends.toNativeArray(), text.length());
}
int getTextLength();
}
@@ -0,0 +1,130 @@
/*
* Copyright 2000-2017 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.diff.tools.util.text;
import com.intellij.diff.util.DiffUtil;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.text.StringUtil;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class LineOffsetsUtil {
public static LineOffsets create(@NotNull Document document) {
return new LineOffsetsDocumentWrapper(document);
}
@NotNull
public static LineOffsets create(@NotNull CharSequence text) {
TIntArrayList ends = new TIntArrayList();
int index = 0;
while (true) {
int lineEnd = StringUtil.indexOf(text, '\n', index);
if (lineEnd != -1) {
ends.add(lineEnd);
index = lineEnd + 1;
}
else {
ends.add(text.length());
break;
}
}
return new LineOffsetsImpl(ends.toNativeArray(), text.length());
}
private static class LineOffsetsImpl implements LineOffsets {
private final int[] myLineEnds;
private final int myTextLength;
private LineOffsetsImpl(int[] lineEnds, int textLength) {
myLineEnds = lineEnds;
myTextLength = textLength;
}
public int getLineStart(int line) {
checkLineIndex(line);
if (line == 0) return 0;
return myLineEnds[line - 1] + 1;
}
public int getLineEnd(int line) {
checkLineIndex(line);
return myLineEnds[line];
}
@Override
public int getLineNumber(int offset) {
if (offset < 0 || offset > getTextLength()) {
throw new IndexOutOfBoundsException("Wrong offset: " + offset + ". Available text length: " + getTextLength());
}
if (offset == 0) return 0;
if (offset == getTextLength()) return getLineCount() - 1;
int bsResult = Arrays.binarySearch(myLineEnds, offset);
return bsResult >= 0 ? bsResult : -bsResult - 1;
}
public int getLineCount() {
return myLineEnds.length;
}
public int getTextLength() {
return myTextLength;
}
private void checkLineIndex(int index) {
if (index < 0 || index >= getLineCount()) {
throw new IndexOutOfBoundsException("Wrong line: " + index + ". Available lines count: " + getLineCount());
}
}
}
private static class LineOffsetsDocumentWrapper implements LineOffsets {
@NotNull private final Document myDocument;
public LineOffsetsDocumentWrapper(@NotNull Document document) {
myDocument = document;
}
@Override
public int getLineStart(int line) {
return myDocument.getLineStartOffset(line);
}
@Override
public int getLineEnd(int line) {
return myDocument.getLineEndOffset(line);
}
@Override
public int getLineNumber(int offset) {
return myDocument.getLineNumber(offset);
}
@Override
public int getLineCount() {
return DiffUtil.getLineCount(myDocument);
}
@Override
public int getTextLength() {
return myDocument.getTextLength();
}
}
}
@@ -57,7 +57,7 @@ public class SimpleThreesideTextDiffProvider extends TextDiffProviderBase {
ComparisonPolicy comparisonPolicy = ignorePolicy.getComparisonPolicy();
List<CharSequence> sequences = ContainerUtil.list(text1, text2, text3);
List<LineOffsets> lineOffsets = ContainerUtil.map(sequences, LineOffsets::create);
List<LineOffsets> lineOffsets = ContainerUtil.map(sequences, LineOffsetsUtil::create);
indicator.checkCanceled();
List<MergeLineFragment> lineFragments = ComparisonManager.getInstance().compareLines(text1, text2, text3, comparisonPolicy, indicator);
@@ -858,16 +858,7 @@ public class DiffUtil {
@NotNull
public static TextRange getLinesRange(@NotNull Document document, int line1, int line2, boolean includeNewline) {
if (line1 == line2) {
int lineStartOffset = line1 < getLineCount(document) ? document.getLineStartOffset(line1) : document.getTextLength();
return new TextRange(lineStartOffset, lineStartOffset);
}
else {
int startOffset = document.getLineStartOffset(line1);
int endOffset = document.getLineEndOffset(line2 - 1);
if (includeNewline && endOffset < document.getTextLength()) endOffset++;
return new TextRange(startOffset, endOffset);
}
return getLinesRange(LineOffsetsUtil.create(document), line1, line2, includeNewline);
}
@NotNull
@@ -16,7 +16,7 @@
package com.intellij.diff.util
import com.intellij.diff.DiffTestCase
import com.intellij.diff.tools.util.text.LineOffsets
import com.intellij.diff.tools.util.text.LineOffsetsUtil
import com.intellij.openapi.editor.impl.DocumentImpl
class LineOffsetsTest : DiffTestCase() {
@@ -40,26 +40,34 @@ class LineOffsetsTest : DiffTestCase() {
}
private fun checkSameAsDocument(text: String) {
val lineOffsets = LineOffsets.create(text)
val document = DocumentImpl(text)
val lineOffsets1 = LineOffsetsUtil.create(DocumentImpl(text))
val lineOffsets2 = LineOffsetsUtil.create(text)
assertEquals(lineOffsets.lineCount, getLineCount(document))
assertEquals(lineOffsets.textLength, document.textLength)
assertEquals(lineOffsets1.lineCount, lineOffsets2.lineCount)
assertEquals(lineOffsets1.textLength, lineOffsets2.textLength)
for (i in 0 until lineOffsets.lineCount) {
assertEquals(lineOffsets.getLineStart(i), document.getLineStartOffset(i))
assertEquals(lineOffsets.getLineEnd(i), document.getLineEndOffset(i))
for (i in 0 until lineOffsets1.lineCount) {
assertEquals(lineOffsets1.getLineStart(i), lineOffsets2.getLineStart(i))
assertEquals(lineOffsets1.getLineEnd(i), lineOffsets2.getLineEnd(i))
}
for (i in 0..lineOffsets1.textLength) {
assertEquals(lineOffsets1.getLineNumber(i), lineOffsets2.getLineNumber(i))
}
}
private fun checkOffsets(text: String, vararg offsets: IntPair) {
val lineOffsets = LineOffsets.create(text)
val lineOffsets = LineOffsetsUtil.create(text)
assertEquals(offsets.size, lineOffsets.lineCount)
offsets.forEachIndexed { i, value ->
assertEquals(lineOffsets.getLineStart(i), value.val1)
assertEquals(lineOffsets.getLineEnd(i), value.val2)
offsets.forEachIndexed { line, value ->
assertEquals(lineOffsets.getLineStart(line), value.val1)
assertEquals(lineOffsets.getLineEnd(line), value.val2)
for (offset in lineOffsets.getLineStart(line)..lineOffsets.getLineEnd(line)) {
assertEquals(line, lineOffsets.getLineNumber(offset))
}
}
}