mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-188650 diff: fix differences computation for empty trailing lines
This commit is contained in:
@@ -61,11 +61,34 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
LineOffsets lineOffsets1 = LineOffsetsUtil.create(text1);
|
||||
LineOffsets lineOffsets2 = LineOffsetsUtil.create(text2);
|
||||
|
||||
List<CharSequence> lineTexts1 = getLineContents(text1, lineOffsets1);
|
||||
List<CharSequence> lineTexts2 = getLineContents(text2, lineOffsets2);
|
||||
return compareLines(text1, text2, lineOffsets1, lineOffsets2, policy, indicator);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<LineFragment> compareLines(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull ComparisonPolicy policy,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
Range range = new Range(0, lineOffsets1.getLineCount(),
|
||||
0, lineOffsets2.getLineCount());
|
||||
return compareLines(range, text1, text2, lineOffsets1, lineOffsets2, policy, indicator);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<LineFragment> compareLines(@NotNull Range range,
|
||||
@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull ComparisonPolicy policy,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
List<CharSequence> lineTexts1 = getLineContents(range.start1, range.end1, text1, lineOffsets1);
|
||||
List<CharSequence> lineTexts2 = getLineContents(range.start2, range.end2, text2, lineOffsets2);
|
||||
|
||||
FairDiffIterable iterable = ByLine.compare(lineTexts1, lineTexts2, policy, indicator);
|
||||
return convertIntoLineFragments(lineOffsets1, lineOffsets2, iterable);
|
||||
return convertIntoLineFragments(range, lineOffsets1, lineOffsets2, iterable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -97,6 +120,41 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
return createInnerFragments(lineFragments, text1, text2, policy, indicator);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<LineFragment> compareLinesInner(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull ComparisonPolicy policy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
List<LineFragment> lineFragments = compareLines(text1, text2, lineOffsets1, lineOffsets2, policy, indicator);
|
||||
if (innerFragments) {
|
||||
return createInnerFragments(lineFragments, text1, text2, policy, indicator);
|
||||
}
|
||||
else {
|
||||
return lineFragments;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<LineFragment> compareLinesInner(@NotNull Range range,
|
||||
@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull ComparisonPolicy policy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
List<LineFragment> lineFragments = compareLines(range, text1, text2, lineOffsets1, lineOffsets2, policy, indicator);
|
||||
if (innerFragments) {
|
||||
return createInnerFragments(lineFragments, text1, text2, policy, indicator);
|
||||
}
|
||||
else {
|
||||
return lineFragments;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<LineFragment> createInnerFragments(@NotNull List<LineFragment> lineFragments,
|
||||
@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@@ -228,15 +286,21 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<LineFragment> convertIntoLineFragments(@NotNull LineOffsets lineOffsets1,
|
||||
public static List<LineFragment> convertIntoLineFragments(@NotNull Range range,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull FairDiffIterable changes) {
|
||||
List<LineFragment> fragments = new ArrayList<>();
|
||||
for (Range ch : changes.iterateChanges()) {
|
||||
IntPair offsets1 = getOffsets(lineOffsets1, ch.start1, ch.end1);
|
||||
IntPair offsets2 = getOffsets(lineOffsets2, ch.start2, ch.end2);
|
||||
int startLine1 = ch.start1 + range.start1;
|
||||
int startLine2 = ch.start2 + range.start2;
|
||||
int endLine1 = ch.end1 + range.start1;
|
||||
int endLine2 = ch.end2 + range.start2;
|
||||
|
||||
fragments.add(new LineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2,
|
||||
IntPair offsets1 = getOffsets(lineOffsets1, startLine1, endLine1);
|
||||
IntPair offsets2 = getOffsets(lineOffsets2, startLine2, endLine2);
|
||||
|
||||
fragments.add(new LineFragmentImpl(startLine1, endLine1, startLine2, endLine2,
|
||||
offsets1.val1, offsets1.val2, offsets2.val1, offsets2.val2));
|
||||
}
|
||||
return fragments;
|
||||
@@ -419,21 +483,26 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
|
||||
@NotNull
|
||||
private static List<CharSequence> getLineContents(@NotNull CharSequence text, @NotNull LineOffsets lineOffsets) {
|
||||
List<CharSequence> lines = new ArrayList<>(lineOffsets.getLineCount());
|
||||
for (int line = 0; line < lineOffsets.getLineCount(); line++) {
|
||||
return getLineContents(0, lineOffsets.getLineCount(), text, lineOffsets);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<CharSequence> getLineContents(int start, int end, @NotNull CharSequence text, @NotNull LineOffsets lineOffsets) {
|
||||
List<CharSequence> lines = new ArrayList<>(end - start);
|
||||
for (int line = start; line < end; line++) {
|
||||
lines.add(new CharSequenceSubSequence(text, lineOffsets.getLineStart(line), lineOffsets.getLineEnd(line)));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<CharSequence> getNotIgnoredLineContents(@NotNull CharSequence text,
|
||||
private static List<CharSequence> getNotIgnoredLineContents(int start, int end,
|
||||
@NotNull CharSequence text,
|
||||
@NotNull LineOffsets lineOffsets,
|
||||
@NotNull BitSet ignored) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
List<CharSequence> lines = new ArrayList<>(lineOffsets.getLineCount());
|
||||
for (int line = 0; line < lineOffsets.getLineCount(); line++) {
|
||||
List<CharSequence> lines = new ArrayList<>(end - start);
|
||||
for (int line = start; line < end; line++) {
|
||||
for (int offset = lineOffsets.getLineStart(line); offset < lineOffsets.getLineEnd(line); offset++) {
|
||||
if (ignored.get(offset)) continue;
|
||||
sb.append(text.charAt(offset));
|
||||
@@ -445,31 +514,43 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public List<LineFragment> compareLinesWithIgnoredRanges(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull BitSet ignored1,
|
||||
@NotNull BitSet ignored2,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
Range range = new Range(0, lineOffsets1.getLineCount(),
|
||||
0, lineOffsets2.getLineCount());
|
||||
return compareLinesWithIgnoredRanges(range, text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2,
|
||||
innerFragments, indicator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two texts by-line and then compare changed fragments by-word
|
||||
*/
|
||||
@NotNull
|
||||
public List<LineFragment> compareLinesWithIgnoredRanges(@NotNull CharSequence text1,
|
||||
public List<LineFragment> compareLinesWithIgnoredRanges(@NotNull Range range,
|
||||
@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull List<TextRange> ignoredRanges1,
|
||||
@NotNull List<TextRange> ignoredRanges2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull BitSet ignored1,
|
||||
@NotNull BitSet ignored2,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
BitSet ignored1 = collectIgnoredRanges(ignoredRanges1);
|
||||
BitSet ignored2 = collectIgnoredRanges(ignoredRanges2);
|
||||
|
||||
LineOffsets lineOffsets1 = LineOffsetsUtil.create(text1);
|
||||
LineOffsets lineOffsets2 = LineOffsetsUtil.create(text2);
|
||||
|
||||
List<CharSequence> lineTexts1 = getNotIgnoredLineContents(text1, lineOffsets1, ignored1);
|
||||
List<CharSequence> lineTexts2 = getNotIgnoredLineContents(text2, lineOffsets2, ignored2);
|
||||
List<CharSequence> lineTexts1 = getNotIgnoredLineContents(range.start1, range.end1, text1, lineOffsets1, ignored1);
|
||||
List<CharSequence> lineTexts2 = getNotIgnoredLineContents(range.start2, range.end2, text2, lineOffsets2, ignored2);
|
||||
|
||||
FairDiffIterable iterable = ByLine.compare(lineTexts1, lineTexts2, ComparisonPolicy.DEFAULT, indicator);
|
||||
|
||||
FairDiffIterable correctedIterable = correctIgnoredRangesSecondStep(iterable, text1, text2, lineOffsets1, lineOffsets2,
|
||||
ignored1, ignored2);
|
||||
|
||||
List<LineFragment> lineFragments = convertIntoLineFragments(lineOffsets1, lineOffsets2, correctedIterable);
|
||||
List<LineFragment> lineFragments = convertIntoLineFragments(range, lineOffsets1, lineOffsets2, correctedIterable);
|
||||
|
||||
if (innerFragments) {
|
||||
lineFragments = createInnerFragments(lineFragments, text1, text2, ComparisonPolicy.DEFAULT, indicator);
|
||||
@@ -497,7 +578,7 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull BitSet ignored1,
|
||||
@NotNull BitSet ignored2) {
|
||||
DiffIterableUtil.ChangeBuilder builder = new DiffIterableUtil.ChangeBuilder(lineOffsets1.getLineCount(), lineOffsets2.getLineCount());
|
||||
DiffIterableUtil.ChangeBuilder builder = new DiffIterableUtil.ChangeBuilder(iterable.getLength1(), iterable.getLength2());
|
||||
for (Range range : iterable.iterateUnchanged()) {
|
||||
int count = range.end1 - range.start1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
+32
-70
@@ -15,23 +15,20 @@
|
||||
*/
|
||||
package com.intellij.diff.tools.util.text;
|
||||
|
||||
import com.intellij.diff.comparison.ComparisonManager;
|
||||
import com.intellij.diff.comparison.ComparisonManagerImpl;
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.comparison.ComparisonUtil;
|
||||
import com.intellij.diff.fragments.LineFragment;
|
||||
import com.intellij.diff.fragments.LineFragmentImpl;
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy;
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy;
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx.DiffComputer;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.Range;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -39,40 +36,32 @@ import java.util.List;
|
||||
|
||||
import static com.intellij.diff.tools.util.base.HighlightPolicy.*;
|
||||
import static com.intellij.diff.tools.util.base.IgnorePolicy.*;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implements TwosideTextDiffProvider {
|
||||
static final DiffComputer DEFAULT_COMPUTER = (text1, text2, policy, innerChanges, indicator) -> {
|
||||
if (innerChanges) {
|
||||
return ComparisonManager.getInstance().compareLinesInner(text1, text2, policy, indicator);
|
||||
}
|
||||
else {
|
||||
return ComparisonManager.getInstance().compareLines(text1, text2, policy, indicator);
|
||||
}
|
||||
};
|
||||
private static final Logger LOG = Logger.getInstance(SimpleTextDiffProvider.class);
|
||||
|
||||
private static final IgnorePolicy[] IGNORE_POLICIES = {DEFAULT, TRIM_WHITESPACES, IGNORE_WHITESPACES, IGNORE_WHITESPACES_CHUNKS};
|
||||
private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, DO_NOT_HIGHLIGHT};
|
||||
|
||||
@NotNull private final DiffComputer myDiffComputer;
|
||||
@Nullable private final DiffComputer myDiffComputer;
|
||||
|
||||
public SimpleTextDiffProvider(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull Disposable disposable) {
|
||||
this(settings, rediff, disposable, DEFAULT_COMPUTER);
|
||||
this(settings, rediff, disposable, null);
|
||||
}
|
||||
|
||||
public SimpleTextDiffProvider(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull DiffComputer diffComputer) {
|
||||
@Nullable DiffComputer diffComputer) {
|
||||
this(settings, rediff, disposable, diffComputer, IGNORE_POLICIES, HIGHLIGHT_POLICIES);
|
||||
}
|
||||
|
||||
private SimpleTextDiffProvider(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull DiffComputer diffComputer,
|
||||
@Nullable DiffComputer diffComputer,
|
||||
@NotNull IgnorePolicy[] ignorePolicies,
|
||||
@NotNull HighlightPolicy[] highlightPolicies) {
|
||||
super(settings, rediff, disposable, ignorePolicies, highlightPolicies);
|
||||
@@ -85,7 +74,7 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull List<Range> linesRanges,
|
||||
@Nullable List<Range> linesRanges,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
@@ -93,73 +82,46 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<List<LineFragment>> compareRange(@NotNull DiffComputer diffComputer,
|
||||
public static List<List<LineFragment>> compareRange(@Nullable DiffComputer diffComputer,
|
||||
@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull List<Range> linesRanges,
|
||||
@Nullable List<Range> linesRanges,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
ComparisonPolicy policy = ignorePolicy.getComparisonPolicy();
|
||||
|
||||
List<List<LineFragment>> result = new ArrayList<>();
|
||||
for (Range range : linesRanges) {
|
||||
CharSequence content1 = DiffUtil.getLinesContent(text1, lineOffsets1, range.start1, range.end1, true);
|
||||
CharSequence content2 = DiffUtil.getLinesContent(text2, lineOffsets2, range.start2, range.end2, true);
|
||||
|
||||
if (range.start1 == range.end1 || range.start2 == range.end2) {
|
||||
boolean isEquals = ComparisonUtil.isEquals(content1, content2, policy);
|
||||
result.add(singletonList(createSimpleFragment(range, lineOffsets1, lineOffsets2, isEquals)));
|
||||
}
|
||||
else {
|
||||
List<LineFragment> fragments = diffComputer.compute(content1, content2, policy, innerFragments, indicator);
|
||||
|
||||
int startOffset1 = lineOffsets1.getLineStart(range.start1);
|
||||
int startOffset2 = lineOffsets2.getLineStart(range.start2);
|
||||
result.add(ContainerUtil.map(fragments, fragment -> {
|
||||
return transferFragment(fragment, range.start1, range.start2, startOffset1, startOffset2);
|
||||
}));
|
||||
}
|
||||
if (diffComputer != null && linesRanges != null) {
|
||||
LOG.error(new Throwable("Unsupported operation: ranged diff with custom DiffComputer - " + diffComputer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static LineFragment createSimpleFragment(@NotNull Range linesRange,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
boolean isEquals) {
|
||||
TextRange textRange1 = DiffUtil.getLinesRange(lineOffsets1, linesRange.start1, linesRange.end1, true);
|
||||
TextRange textRange2 = DiffUtil.getLinesRange(lineOffsets2, linesRange.start2, linesRange.end2, true);
|
||||
|
||||
return new LineFragmentImpl(linesRange.start1, linesRange.end1,
|
||||
linesRange.start2, linesRange.end2,
|
||||
textRange1.getStartOffset(), textRange1.getEndOffset(),
|
||||
textRange2.getStartOffset(), textRange2.getEndOffset(),
|
||||
isEquals ? Collections.emptyList() : null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static LineFragment transferFragment(@NotNull LineFragment fragment,
|
||||
int startLine1, int startLine2,
|
||||
int startOffset1, int startOffset2) {
|
||||
if (startLine1 == 0 && startLine2 == 0 && startOffset1 == 0 && startOffset2 == 0) return fragment;
|
||||
return new LineFragmentImpl(fragment.getStartLine1() + startLine1, fragment.getEndLine1() + startLine1,
|
||||
fragment.getStartLine2() + startLine2, fragment.getEndLine2() + startLine2,
|
||||
fragment.getStartOffset1() + startOffset1, fragment.getEndOffset1() + startOffset1,
|
||||
fragment.getStartOffset2() + startOffset2, fragment.getEndOffset2() + startOffset2,
|
||||
fragment.getInnerFragments());
|
||||
ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl();
|
||||
if (diffComputer != null || linesRanges == null) {
|
||||
List<LineFragment> fragments = comparisonManager.compareLinesInner(text1, text2, lineOffsets1, lineOffsets2,
|
||||
policy, innerFragments, indicator);
|
||||
return Collections.singletonList(fragments);
|
||||
}
|
||||
else {
|
||||
List<List<LineFragment>> result = new ArrayList<>();
|
||||
for (Range range : linesRanges) {
|
||||
result.add(comparisonManager.compareLinesInner(range, text1, text2, lineOffsets1, lineOffsets2,
|
||||
policy, innerFragments, indicator));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NoIgnore extends SimpleTextDiffProvider implements TwosideTextDiffProvider.NoIgnore {
|
||||
public NoIgnore(@NotNull TextDiffSettings settings, @NotNull Runnable rediff, @NotNull Disposable disposable) {
|
||||
this(settings, rediff, disposable, DEFAULT_COMPUTER);
|
||||
this(settings, rediff, disposable, null);
|
||||
}
|
||||
|
||||
public NoIgnore(@NotNull TextDiffSettings settings, @NotNull Runnable rediff, @NotNull Disposable disposable, @NotNull DiffComputer diffComputer) {
|
||||
public NoIgnore(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull Disposable disposable,
|
||||
@Nullable DiffComputer diffComputer) {
|
||||
super(settings, rediff, disposable, diffComputer, IGNORE_POLICIES, ArrayUtil.remove(HIGHLIGHT_POLICIES, DO_NOT_HIGHLIGHT));
|
||||
}
|
||||
|
||||
|
||||
+20
-39
@@ -23,7 +23,6 @@ import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy;
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy;
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.Range;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.openapi.Disposable;
|
||||
@@ -32,11 +31,12 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.diff.tools.util.base.HighlightPolicy.*;
|
||||
@@ -116,7 +116,7 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull List<Range> linesRanges,
|
||||
@Nullable List<Range> linesRanges,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
@@ -124,8 +124,7 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement
|
||||
return compareIgnoreFormatting(text1, text2, lineOffsets1, lineOffsets2, linesRanges, innerFragments, indicator);
|
||||
}
|
||||
else {
|
||||
return SimpleTextDiffProvider.compareRange(SimpleTextDiffProvider.DEFAULT_COMPUTER,
|
||||
text1, text2, lineOffsets1, lineOffsets2, linesRanges,
|
||||
return SimpleTextDiffProvider.compareRange(null, text1, text2, lineOffsets1, lineOffsets2, linesRanges,
|
||||
ignorePolicy, innerFragments, indicator);
|
||||
}
|
||||
}
|
||||
@@ -135,47 +134,29 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull List<Range> linesRanges,
|
||||
@Nullable List<Range> linesRanges,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
List<TextRange> ranges1 = myProvider.getIgnoredRanges(myProject, text1, myContent1);
|
||||
List<TextRange> ranges2 = myProvider.getIgnoredRanges(myProject, text2, myContent2);
|
||||
List<TextRange> ignoredRanges1 = myProvider.getIgnoredRanges(myProject, text1, myContent1);
|
||||
List<TextRange> ignoredRanges2 = myProvider.getIgnoredRanges(myProject, text2, myContent2);
|
||||
|
||||
List<List<LineFragment>> result = new ArrayList<>();
|
||||
for (Range range : linesRanges) {
|
||||
TextRange offsets1 = DiffUtil.getLinesRange(lineOffsets1, range.start1, range.end1, true);
|
||||
TextRange offsets2 = DiffUtil.getLinesRange(lineOffsets2, range.start2, range.end2, true);
|
||||
BitSet ignored1 = ComparisonManagerImpl.collectIgnoredRanges(ignoredRanges1);
|
||||
BitSet ignored2 = ComparisonManagerImpl.collectIgnoredRanges(ignoredRanges2);
|
||||
|
||||
CharSequence subText1 = offsets1.subSequence(text1);
|
||||
CharSequence subText2 = offsets2.subSequence(text2);
|
||||
|
||||
List<TextRange> subRanges1 = getSubRanges(ranges1, offsets1);
|
||||
List<TextRange> subRanges2 = getSubRanges(ranges2, offsets2);
|
||||
|
||||
ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl();
|
||||
List<LineFragment> fragments = comparisonManager.compareLinesWithIgnoredRanges(subText1, subText2, subRanges1, subRanges2,
|
||||
innerFragments, indicator);
|
||||
|
||||
int startOffset1 = offsets1.getStartOffset();
|
||||
int startOffset2 = offsets2.getStartOffset();
|
||||
result.add(ContainerUtil.map(fragments, fragment -> {
|
||||
return SimpleTextDiffProvider.transferFragment(fragment, range.start1, range.start2, startOffset1, startOffset2);
|
||||
}));
|
||||
ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl();
|
||||
if (linesRanges == null) {
|
||||
List<LineFragment> fragments = comparisonManager.compareLinesWithIgnoredRanges(text1, text2, lineOffsets1, lineOffsets2,
|
||||
ignored1, ignored2, innerFragments, indicator);
|
||||
return Collections.singletonList(fragments);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<TextRange> getSubRanges(@NotNull List<TextRange> ignoredRanges, @NotNull TextRange offsets) {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
for (TextRange range : ignoredRanges) {
|
||||
TextRange intersection = range.intersection(offsets);
|
||||
if (intersection != null) {
|
||||
result.add(new TextRange(intersection.getStartOffset() - offsets.getStartOffset(),
|
||||
intersection.getEndOffset() - offsets.getStartOffset()));
|
||||
else {
|
||||
List<List<LineFragment>> result = new ArrayList<>();
|
||||
for (Range range : linesRanges) {
|
||||
result.add(comparisonManager.compareLinesWithIgnoredRanges(range, text1, text2, lineOffsets1, lineOffsets2,
|
||||
ignored1, ignored2, innerFragments, indicator));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+4
-7
@@ -30,8 +30,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
abstract class TwosideTextDiffProviderBase extends TextDiffProviderBase implements TwosideTextDiffProvider {
|
||||
protected TwosideTextDiffProviderBase(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@@ -49,8 +47,7 @@ abstract class TwosideTextDiffProviderBase extends TextDiffProviderBase implemen
|
||||
LineOffsets lineOffsets1 = LineOffsetsUtil.create(text1);
|
||||
LineOffsets lineOffsets2 = LineOffsetsUtil.create(text2);
|
||||
|
||||
Range linesRange = new Range(0, lineOffsets1.getLineCount(), 0, lineOffsets2.getLineCount());
|
||||
List<List<LineFragment>> fragments = doCompare(text1, text2, lineOffsets1, lineOffsets2, singletonList(linesRange), indicator);
|
||||
List<List<LineFragment>> fragments = doCompare(text1, text2, lineOffsets1, lineOffsets2, null, indicator);
|
||||
|
||||
if (fragments == null) return null;
|
||||
|
||||
@@ -74,7 +71,7 @@ abstract class TwosideTextDiffProviderBase extends TextDiffProviderBase implemen
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull List<Range> linesRanges,
|
||||
@Nullable List<Range> linesRanges,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
IgnorePolicy ignorePolicy = getIgnorePolicy();
|
||||
HighlightPolicy highlightPolicy = getHighlightPolicy();
|
||||
@@ -89,7 +86,7 @@ abstract class TwosideTextDiffProviderBase extends TextDiffProviderBase implemen
|
||||
indicator.checkCanceled();
|
||||
List<List<LineFragment>> fragments = doCompare(text1, text2, lineOffsets1, lineOffsets2, linesRanges,
|
||||
ignorePolicy, innerFragments, indicator);
|
||||
assert fragments.size() == linesRanges.size();
|
||||
assert fragments.size() == (linesRanges != null ? linesRanges.size() : 1);
|
||||
|
||||
indicator.checkCanceled();
|
||||
return ContainerUtil.map(fragments, rangeFragments -> {
|
||||
@@ -103,7 +100,7 @@ abstract class TwosideTextDiffProviderBase extends TextDiffProviderBase implemen
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull LineOffsets lineOffsets1,
|
||||
@NotNull LineOffsets lineOffsets2,
|
||||
@NotNull List<Range> linesRanges,
|
||||
@Nullable List<Range> linesRanges,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator);
|
||||
|
||||
@@ -133,7 +133,7 @@ abstract class DiffTestCase : TestCase() {
|
||||
|
||||
fun textToReadableFormat(text: CharSequence?): String {
|
||||
if (text == null) return "null"
|
||||
return "'" + text.toString().replace('\n', '*').replace('\t', '+') + "'"
|
||||
return "\"" + text.toString().replace('\n', '*').replace('\t', '+') + "\""
|
||||
}
|
||||
|
||||
fun parseSource(string: CharSequence): String = string.toString().replace('_', '\n')
|
||||
|
||||
@@ -15,7 +15,14 @@
|
||||
*/
|
||||
package com.intellij.diff
|
||||
|
||||
import com.intellij.diff.fragments.LineFragment
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder
|
||||
import com.intellij.diff.tools.util.text.SimpleTextDiffProvider
|
||||
import com.intellij.diff.util.Range
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture
|
||||
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
|
||||
import com.intellij.testFramework.runInEdtAndWait
|
||||
@@ -47,4 +54,21 @@ abstract class HeavyDiffTestCase : DiffTestCase() {
|
||||
super.runBare()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected fun compareExplicitBlocks(text1: CharSequence, text2: CharSequence, ranges: List<Range>,
|
||||
highlightPolicy: HighlightPolicy, ignorePolicy: IgnorePolicy): List<LineFragment> {
|
||||
val settings = TextDiffSettingsHolder.TextDiffSettings()
|
||||
settings.highlightPolicy = highlightPolicy
|
||||
settings.ignorePolicy = ignorePolicy
|
||||
|
||||
val disposable = Disposer.newDisposable()
|
||||
try {
|
||||
val diffProvider = SimpleTextDiffProvider(settings, Runnable {}, disposable)
|
||||
return diffProvider.compare(text1, text2, ranges, INDICATOR)!!.flatMap { it }
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.diff.comparison
|
||||
|
||||
import com.intellij.diff.HeavyDiffTestCase
|
||||
import com.intellij.diff.fragments.DiffFragmentImpl
|
||||
import com.intellij.diff.fragments.LineFragment
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy
|
||||
import com.intellij.diff.util.Range
|
||||
import com.intellij.diff.util.Side
|
||||
import java.util.*
|
||||
|
||||
class BlocksComparisonUtilTest : HeavyDiffTestCase() {
|
||||
fun `test simple blocks`() {
|
||||
Test("X_a_Y_c_Z", "X_ a__Y__ c_Z")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.default()
|
||||
|
||||
Test("X_a_Y_c_Z", "X_ a__Y__ c_Z")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
Range(2, 2, 2, 3), Range(3, 3, 4, 5))
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("X_a_Y_c_Z", "X_ a__Y__ c_Z")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
)
|
||||
.ignore_chunks()
|
||||
}
|
||||
|
||||
fun `test changes outside of blocks are ignored`() {
|
||||
Test("X_a_Y_c_Z", "N_ a__N__ c_N")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.default()
|
||||
|
||||
Test("X_X_X_a_Y_c_Z", "N_ a__N__ c_N")
|
||||
.blocks(
|
||||
Range(5, 6, 4, 6))
|
||||
.expected(
|
||||
Range(5, 6, 4, 6))
|
||||
.default()
|
||||
}
|
||||
|
||||
fun `test multiple fragments in a block`() {
|
||||
Test("X_a_b_c_Y", "X_a1_ b_c2_Y")
|
||||
.blocks(
|
||||
Range(1, 4, 1, 4))
|
||||
.expected(
|
||||
Range(1, 4, 1, 4))
|
||||
.default()
|
||||
|
||||
Test("X_a_b_c_Y", "X_a1_ b_c2_Y")
|
||||
.blocks(
|
||||
Range(1, 4, 1, 4))
|
||||
.expected(
|
||||
Range(1, 2, 1, 2), Range(3, 4, 3, 4))
|
||||
.trim()
|
||||
.ignore()
|
||||
.ignore_chunks()
|
||||
}
|
||||
|
||||
fun `test fragments respect ignore options`() {
|
||||
Test("X_a_b_c_Y", "X_a1_ b_c2_Y")
|
||||
.blocks(
|
||||
Range(1, 4, 1, 4))
|
||||
.expected(
|
||||
Range(1, 4, 1, 4))
|
||||
.default()
|
||||
|
||||
Test("X_Y", "X_a_c_Y")
|
||||
.blocks(
|
||||
Range(1, 1, 1, 3))
|
||||
.expected(
|
||||
Range(1, 1, 1, 3))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
.ignore_chunks()
|
||||
|
||||
Test("X_Y", "X___Y")
|
||||
.blocks(
|
||||
Range(1, 1, 1, 3))
|
||||
.expected(
|
||||
Range(1, 1, 1, 3))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("X_Y", "X___Y")
|
||||
.blocks(
|
||||
Range(1, 1, 1, 3))
|
||||
.expected(
|
||||
)
|
||||
.ignore_chunks()
|
||||
}
|
||||
|
||||
fun `test leading-trailing empty lines`() {
|
||||
Test("X_a__Y", "X__y_Y")
|
||||
.blocks(
|
||||
Range(1, 3, 1, 3))
|
||||
.expected(
|
||||
Range(1, 2, 1, 1), Range(3, 3, 2, 3))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("X_a__", "X__y_")
|
||||
.blocks(
|
||||
Range(1, 3, 1, 3))
|
||||
.expected(
|
||||
Range(1, 2, 1, 1), Range(3, 3, 2, 3))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("a__", "_y_")
|
||||
.blocks(
|
||||
Range(0, 2, 0, 2))
|
||||
.expected(
|
||||
Range(0, 1, 0, 0), Range(2, 2, 1, 2))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("a__", "_y_")
|
||||
.blocks(
|
||||
Range(0, 3, 0, 3))
|
||||
.expected(
|
||||
Range(0, 1, 0, 0), Range(2, 2, 1, 2))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("a_", "_y")
|
||||
.blocks(
|
||||
Range(0, 2, 0, 2))
|
||||
.expected(
|
||||
Range(0, 1, 0, 0), Range(2, 2, 1, 2))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
}
|
||||
|
||||
fun `test inner changes`() {
|
||||
Test("X_a_Y", "X_b_Y",
|
||||
" - ", " - ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 2)
|
||||
)
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
.ignore_chunks()
|
||||
|
||||
Test("X_a_Y", "X_ a_b_Y",
|
||||
" ", " -- -- ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3)
|
||||
)
|
||||
.default()
|
||||
|
||||
Test("X_a_Y", "X_ a_b_Y",
|
||||
" ", " -- ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3)
|
||||
)
|
||||
.expected(
|
||||
Range(2, 2, 2, 3)
|
||||
)
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("X_a b_Y", "X_a b_Y",
|
||||
" ", " - ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 2)
|
||||
)
|
||||
.default()
|
||||
.trim()
|
||||
|
||||
Test("X_a b_Y", "X_a b_Y",
|
||||
" ", " ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 2)
|
||||
)
|
||||
.ignore()
|
||||
.ignore_chunks()
|
||||
|
||||
Test("X_a_Y_c_Z", "X_ a__Y__ c_Z",
|
||||
" ", " - - -- ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.default()
|
||||
|
||||
Test("X_a_Y_c_Z", "X_ a__Y__ c_Z",
|
||||
" ", " - - ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
Range(2, 2, 2, 3), Range(3, 3, 4, 5))
|
||||
.trim()
|
||||
|
||||
Test("X_a_Y_c_Z", "X_ a__Y__ c_Z",
|
||||
" ", " ")
|
||||
.blocks(
|
||||
Range(1, 2, 1, 3), Range(3, 4, 4, 6))
|
||||
.expected(
|
||||
Range(2, 2, 2, 3), Range(3, 3, 4, 5))
|
||||
.ignore()
|
||||
}
|
||||
|
||||
fun `test inner changes for inserted empty line`() {
|
||||
Test("X_Y", "X_ _Y",
|
||||
" ", " -- ")
|
||||
.blocks(
|
||||
Range(1, 1, 1, 2)
|
||||
)
|
||||
.trim()
|
||||
.default()
|
||||
|
||||
Test("X_Y", "X_ _Y",
|
||||
" ", " ")
|
||||
.blocks(
|
||||
Range(1, 1, 1, 2)
|
||||
)
|
||||
.expected(
|
||||
Range(1, 1, 1, 2)
|
||||
)
|
||||
.ignore()
|
||||
|
||||
Test("X_Y", "X_ _Y",
|
||||
" ", " ")
|
||||
.blocks(
|
||||
Range(1, 1, 1, 2)
|
||||
)
|
||||
.expected(
|
||||
)
|
||||
.ignore_chunks()
|
||||
}
|
||||
|
||||
fun `test inner changes with leading-trailing empty lines`() {
|
||||
Test("X_a__Y", "X__y_Y",
|
||||
" -- ", " -- ")
|
||||
.blocks(
|
||||
Range(1, 3, 1, 3))
|
||||
.expected(
|
||||
Range(1, 2, 1, 1), Range(3, 3, 2, 3))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
|
||||
Test("a__", "_y_",
|
||||
"-- ", " --")
|
||||
.blocks(
|
||||
Range(0, 2, 0, 2))
|
||||
.expected(
|
||||
Range(0, 1, 0, 0), Range(2, 2, 1, 2))
|
||||
.default()
|
||||
.trim()
|
||||
.ignore()
|
||||
}
|
||||
|
||||
private inner class Test(val input1: String, val input2: String,
|
||||
val inner1: String? = null, val inner2: String? = null) {
|
||||
var blocks: List<Range>? = null
|
||||
var expected: List<Range>? = null
|
||||
|
||||
fun expected(vararg ranges: Range): Test {
|
||||
expected = ranges.toList()
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
fun blocks(vararg ranges: Range): Test {
|
||||
blocks = ranges.toList()
|
||||
return this
|
||||
}
|
||||
|
||||
fun default(): Test {
|
||||
doTest(IgnorePolicy.DEFAULT)
|
||||
return this
|
||||
}
|
||||
|
||||
fun trim(): Test {
|
||||
doTest(IgnorePolicy.TRIM_WHITESPACES)
|
||||
return this
|
||||
}
|
||||
|
||||
fun ignore(): Test {
|
||||
doTest(IgnorePolicy.IGNORE_WHITESPACES)
|
||||
return this
|
||||
}
|
||||
|
||||
fun ignore_chunks(): Test {
|
||||
doTest(IgnorePolicy.IGNORE_WHITESPACES_CHUNKS)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
private fun doTest(ignorePolicy: IgnorePolicy) {
|
||||
if (expected == null && (inner1 == null || inner2 == null)) throw IllegalArgumentException()
|
||||
|
||||
if (expected != null) {
|
||||
val fragments1 = compareExplicitBlocks(parseSource(input1), parseSource(input2), blocks!!, HighlightPolicy.BY_LINE, ignorePolicy)
|
||||
val fragments2 = compareExplicitBlocks(parseSource(input1), parseSource(input2), blocks!!, HighlightPolicy.BY_WORD, ignorePolicy)
|
||||
|
||||
assertEquals(expected!!, fragments1.toRanges())
|
||||
assertEquals(expected!!, fragments2.toRanges())
|
||||
}
|
||||
|
||||
if (inner1 != null && inner2 != null) {
|
||||
val fragments = compareExplicitBlocks(parseSource(input1), parseSource(input2), blocks!!, HighlightPolicy.BY_WORD, ignorePolicy)
|
||||
|
||||
val expected1 = parseInnerExpected(inner1)
|
||||
val expected2 = parseInnerExpected(inner2)
|
||||
|
||||
val actual1 = parseInnerActual(fragments, Side.LEFT)
|
||||
val actual2 = parseInnerActual(fragments, Side.RIGHT)
|
||||
|
||||
assertEquals(expected1, actual1)
|
||||
assertEquals(expected2, actual2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseInnerExpected(inner: String): BitSet {
|
||||
val set = BitSet()
|
||||
inner.forEachIndexed { index, c ->
|
||||
if (c == '-') set.set(index)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
private fun parseInnerActual(fragments: List<LineFragment>, side: Side): BitSet {
|
||||
val set = BitSet()
|
||||
fragments.flatMap { fragment ->
|
||||
fragment.innerFragments?.map {
|
||||
DiffFragmentImpl(fragment.startOffset1 + it.startOffset1, fragment.startOffset1 + it.endOffset1,
|
||||
fragment.startOffset2 + it.startOffset2, fragment.startOffset2 + it.endOffset2)
|
||||
} ?: listOf(fragment)
|
||||
}.forEach {
|
||||
val start = side.getStartOffset(it)
|
||||
val end = side.getEndOffset(it)
|
||||
set.set(start, end)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
private fun List<LineFragment>.toRanges(): List<Range> {
|
||||
return this.map { Range(it.startLine1, it.endLine1, it.startLine2, it.endLine2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,23 @@
|
||||
package com.intellij.diff.comparison
|
||||
|
||||
import com.intellij.diff.DiffTestCase
|
||||
import com.intellij.diff.HeavyDiffTestCase
|
||||
import com.intellij.diff.fragments.DiffFragment
|
||||
import com.intellij.diff.fragments.LineFragment
|
||||
import com.intellij.diff.fragments.MergeLineFragment
|
||||
import com.intellij.diff.fragments.MergeWordFragment
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy
|
||||
import com.intellij.diff.util.DiffUtil
|
||||
import com.intellij.diff.util.Range
|
||||
import com.intellij.diff.util.ThreeSide
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl
|
||||
import com.intellij.openapi.util.Couple
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vcs.ex.createRanges
|
||||
|
||||
class ComparisonUtilAutoTest : DiffTestCase() {
|
||||
class ComparisonUtilAutoTest : HeavyDiffTestCase() {
|
||||
val RUNS = 30
|
||||
val MAX_LENGTH = 300
|
||||
|
||||
@@ -51,6 +56,10 @@ class ComparisonUtilAutoTest : DiffTestCase() {
|
||||
doTestLineTrimSquashed(System.currentTimeMillis(), RUNS, MAX_LENGTH)
|
||||
}
|
||||
|
||||
fun testExplicitBlocks() {
|
||||
doTestExplicitBlocks(System.currentTimeMillis(), RUNS, MAX_LENGTH)
|
||||
}
|
||||
|
||||
fun testMerge() {
|
||||
doTestMerge(System.currentTimeMillis(), RUNS, MAX_LENGTH)
|
||||
}
|
||||
@@ -131,6 +140,31 @@ class ComparisonUtilAutoTest : DiffTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun doTestExplicitBlocks(seed: Long, runs: Int, maxLength: Int) {
|
||||
val ignorePolicies = listOf(IgnorePolicy.DEFAULT, IgnorePolicy.TRIM_WHITESPACES, IgnorePolicy.IGNORE_WHITESPACES, IgnorePolicy.IGNORE_WHITESPACES_CHUNKS)
|
||||
val highlightPolicies = listOf(HighlightPolicy.BY_LINE, HighlightPolicy.BY_WORD, HighlightPolicy.BY_WORD_SPLIT)
|
||||
|
||||
doTest(seed, runs, maxLength) { text1, text2, debugData ->
|
||||
for (highlightPolicy in highlightPolicies) {
|
||||
for (ignorePolicy in ignorePolicies) {
|
||||
debugData.put("HighlightPolicy", highlightPolicy)
|
||||
debugData.put("IgnorePolicy", ignorePolicy)
|
||||
|
||||
val sequence1 = text1.charsSequence
|
||||
val sequence2 = text2.charsSequence
|
||||
|
||||
val ranges = createRanges(sequence2, sequence1).map { Range(it.vcsLine1, it.vcsLine2, it.line1, it.line2) }
|
||||
debugData.put("Ranges", ranges)
|
||||
|
||||
val fragments = compareExplicitBlocks(sequence1, sequence2, ranges, highlightPolicy, ignorePolicy)
|
||||
debugData.put("Fragments", fragments)
|
||||
|
||||
checkResultLine(text1, text2, fragments, ignorePolicy.comparisonPolicy, !highlightPolicy.isShouldSquash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doTestMerge(seed: Long, runs: Int, maxLength: Int) {
|
||||
val policies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES)
|
||||
|
||||
@@ -157,6 +191,16 @@ class ComparisonUtilAutoTest : DiffTestCase() {
|
||||
|
||||
private fun doTest(seed: Long, runs: Int, maxLength: Int, policies: List<ComparisonPolicy>,
|
||||
test: (Document, Document, ComparisonPolicy, DiffTestCase.DebugData) -> Unit) {
|
||||
doTest(seed, runs, maxLength) { text1, text2, debugData ->
|
||||
for (comparisonPolicy in policies) {
|
||||
debugData.put("Policy", comparisonPolicy)
|
||||
test(text1, text2, comparisonPolicy, debugData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doTest(seed: Long, runs: Int, maxLength: Int,
|
||||
test: (Document, Document, DiffTestCase.DebugData) -> Unit) {
|
||||
doAutoTest(seed, runs) { debugData ->
|
||||
debugData.put("MaxLength", maxLength)
|
||||
|
||||
@@ -166,10 +210,7 @@ class ComparisonUtilAutoTest : DiffTestCase() {
|
||||
debugData.put("Text1", textToReadableFormat(text1.charsSequence))
|
||||
debugData.put("Text2", textToReadableFormat(text2.charsSequence))
|
||||
|
||||
for (comparisonPolicy in policies) {
|
||||
debugData.put("Policy", comparisonPolicy)
|
||||
test(text1, text2, comparisonPolicy, debugData)
|
||||
}
|
||||
test(text1, text2, debugData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ package com.intellij.diff.comparison
|
||||
|
||||
import com.intellij.diff.DiffTestCase
|
||||
import com.intellij.diff.fragments.LineFragment
|
||||
import com.intellij.diff.tools.util.text.LineOffsetsUtil
|
||||
import com.intellij.diff.util.IntPair
|
||||
import com.intellij.diff.util.Range
|
||||
import com.intellij.openapi.util.Couple
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import java.util.*
|
||||
@@ -411,6 +413,89 @@ class IgnoreComparisonUtilTest : DiffTestCase() {
|
||||
.run()
|
||||
}
|
||||
|
||||
fun `test explicit blocks`() {
|
||||
Test("X_a_Y_b_Z", "X_a 1 c_Y_b 1 c_Z",
|
||||
" ", " +++ +++ ",
|
||||
" ", " - ")
|
||||
.ranged(Range(1, 2, 1, 2))
|
||||
.changedLinesNumber(1, 1)
|
||||
.run()
|
||||
|
||||
Test("X_a_Y_b_Z", "X_a 1 c_Y_b 1 c_Z",
|
||||
" ", " ++++ +++ ",
|
||||
" ", " ")
|
||||
.ranged(Range(1, 2, 1, 2))
|
||||
.changedLinesNumber(0, 0)
|
||||
.run()
|
||||
|
||||
Test("X_a_Y_b_Z", "X_a 1 c_Y_b 1 c_Z",
|
||||
" ", " + ",
|
||||
" ", " --- ")
|
||||
.ranged(Range(3, 4, 3, 4))
|
||||
.changedLinesNumber(1, 1)
|
||||
.run()
|
||||
|
||||
Test("X_a_Y_b_Z", "X_a 1 c_Y_b 1 c_Z",
|
||||
" ", " + ",
|
||||
" - ", " ---- ")
|
||||
.ranged(Range(1, 2, 3, 4))
|
||||
.changedLinesNumber(1, 1)
|
||||
.run()
|
||||
|
||||
Test("X_a_Y_b_Z", "Y_b 1 c_Z",
|
||||
" ", " + ",
|
||||
" ", " --- ")
|
||||
.ranged(Range(3, 4, 1, 2))
|
||||
.changedLinesNumber(1, 1)
|
||||
.run()
|
||||
|
||||
Test("X_a_Y_b_Z", "Y_c_d_b_Z",
|
||||
" ", " ++++ ",
|
||||
" ", " ")
|
||||
.ranged(Range(3, 4, 1, 4))
|
||||
.changedLinesNumber(0, 0)
|
||||
.run()
|
||||
|
||||
Test("X_a_Y_Z", "Y_c_d_Z",
|
||||
" ", " ++++ ",
|
||||
" ", " ")
|
||||
.ranged(Range(3, 3, 1, 3))
|
||||
.changedLinesNumber(0, 0)
|
||||
.run()
|
||||
|
||||
|
||||
Test("X_a_Y_Z", "Y_c_d_Z",
|
||||
" ", " ",
|
||||
" ", " ---- ")
|
||||
.ranged(Range(3, 3, 1, 3))
|
||||
.changedLinesNumber(0, 2)
|
||||
.run()
|
||||
|
||||
Test("X_W 1 W_Y_W 2 W_Z", "X_W 3 W_B_W 4 W_Z",
|
||||
" + + ", " + + ",
|
||||
" -- ", " -- ")
|
||||
.ranged(Range(2, 4, 2, 4))
|
||||
.changedLinesNumber(1, 1)
|
||||
.noInnerChanges()
|
||||
.run()
|
||||
|
||||
Test("X_W 1 W_Y_W 2 W_Z", "X_W 3 W_B_W 4 W_Z",
|
||||
" + + ", " + + ",
|
||||
" -- ", " -- ")
|
||||
.ranged(Range(1, 5, 1, 5))
|
||||
.changedLinesNumber(1, 1)
|
||||
.noInnerChanges()
|
||||
.run()
|
||||
|
||||
Test("X_W 1 W_Y_W 2 W_Z", "X_W 3 W_B_W 4 W_Z",
|
||||
" + + ", " + + ",
|
||||
" ", " ")
|
||||
.ranged(Range(1, 2, 3, 4))
|
||||
.changedLinesNumber(0, 0)
|
||||
.noInnerChanges()
|
||||
.run()
|
||||
}
|
||||
|
||||
private inner class Test(val input1: String, val input2: String,
|
||||
ignored1: String, ignored2: String,
|
||||
result1: String, result2: String) {
|
||||
@@ -421,6 +506,7 @@ class IgnoreComparisonUtilTest : DiffTestCase() {
|
||||
|
||||
private var inner = true
|
||||
private var changedLines: IntPair? = null
|
||||
private var range: Range? = null
|
||||
|
||||
fun noInnerChanges(): Test {
|
||||
inner = false
|
||||
@@ -432,6 +518,11 @@ class IgnoreComparisonUtilTest : DiffTestCase() {
|
||||
return this
|
||||
}
|
||||
|
||||
fun ranged(range: Range): Test {
|
||||
this.range = range
|
||||
return this
|
||||
}
|
||||
|
||||
fun run() {
|
||||
assertEquals(input1.length, ignored1.length)
|
||||
assertEquals(input1.length, result1.length)
|
||||
@@ -444,7 +535,18 @@ class IgnoreComparisonUtilTest : DiffTestCase() {
|
||||
val ignoredRanges1 = parseIgnored(ignored1)
|
||||
val ignoredRanges2 = parseIgnored(ignored2)
|
||||
|
||||
val result = MANAGER.compareLinesWithIgnoredRanges(text1, text2, ignoredRanges1, ignoredRanges2, inner, INDICATOR)
|
||||
val ignored1 = ComparisonManagerImpl.collectIgnoredRanges(ignoredRanges1)
|
||||
val ignored2 = ComparisonManagerImpl.collectIgnoredRanges(ignoredRanges2)
|
||||
|
||||
val lineOffsets1 = LineOffsetsUtil.create(text1)
|
||||
val lineOffsets2 = LineOffsetsUtil.create(text2)
|
||||
|
||||
val result = if (range != null) {
|
||||
MANAGER.compareLinesWithIgnoredRanges(range!!, text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, inner, INDICATOR)
|
||||
}
|
||||
else {
|
||||
MANAGER.compareLinesWithIgnoredRanges(text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, inner, INDICATOR)
|
||||
}
|
||||
|
||||
val expected = Couple(parseExpected(result1), parseExpected(result2))
|
||||
val actual = parseActual(result)
|
||||
|
||||
Reference in New Issue
Block a user