From 6ba9ed4eaf465d2b9eef48eb899f7778432b8415 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 13 Mar 2017 12:51:40 +0300 Subject: [PATCH] diff: allow to create LineOffsets from Document --- .../intellij/diff/merge/TextMergeViewer.java | 3 +- .../diff/tools/util/text/LineOffsets.java | 59 +------- .../diff/tools/util/text/LineOffsetsUtil.java | 130 ++++++++++++++++++ .../text/SimpleThreesideTextDiffProvider.java | 2 +- .../src/com/intellij/diff/util/DiffUtil.java | 11 +- .../com/intellij/diff/util/LineOffsetsTest.kt | 32 +++-- 6 files changed, 160 insertions(+), 77 deletions(-) create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsetsUtil.java diff --git a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java index a6ebdc48cc8a..7d1274659e29 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java @@ -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 = ContainerUtil.map(sequences, LineOffsets::create); + List lineOffsets = ContainerUtil.map(sequences, LineOffsetsUtil::create); ComparisonManager manager = ComparisonManager.getInstance(); List lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2), diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsets.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsets.java index 7b952afc6f98..3c72d8fe255b 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsets.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsets.java @@ -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(); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsetsUtil.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsetsUtil.java new file mode 100644 index 000000000000..273cce8b5aab --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/LineOffsetsUtil.java @@ -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(); + } + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java index 3fffcdc1e4e6..779174cd4bc2 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java @@ -57,7 +57,7 @@ public class SimpleThreesideTextDiffProvider extends TextDiffProviderBase { ComparisonPolicy comparisonPolicy = ignorePolicy.getComparisonPolicy(); List sequences = ContainerUtil.list(text1, text2, text3); - List lineOffsets = ContainerUtil.map(sequences, LineOffsets::create); + List lineOffsets = ContainerUtil.map(sequences, LineOffsetsUtil::create); indicator.checkCanceled(); List lineFragments = ComparisonManager.getInstance().compareLines(text1, text2, text3, comparisonPolicy, indicator); diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index 18c5915c1a7e..05257705b957 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -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 diff --git a/platform/diff-impl/tests/com/intellij/diff/util/LineOffsetsTest.kt b/platform/diff-impl/tests/com/intellij/diff/util/LineOffsetsTest.kt index dad6cc51773a..cd698d0db24a 100644 --- a/platform/diff-impl/tests/com/intellij/diff/util/LineOffsetsTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/util/LineOffsetsTest.kt @@ -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)) + } } }