/* * 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.diff.comparison; import com.intellij.diff.comparison.iterables.FairDiffIterable; import com.intellij.diff.util.MergeRange; import com.intellij.diff.util.Range; import com.intellij.diff.util.Side; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.registry.Registry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ComparisonMergeUtil { @NotNull public static List<MergeRange> buildFair(@NotNull FairDiffIterable fragments1, @NotNull FairDiffIterable fragments2, @NotNull ProgressIndicator indicator) { assert fragments1.getLength1() == fragments2.getLength1(); return new FairMergeBuilder().execute(fragments1, fragments2); } private static class FairMergeBuilder { @NotNull private final ChangeBuilder myChangesBuilder = new ChangeBuilder(); @NotNull public List<MergeRange> execute(@NotNull FairDiffIterable fragments1, @NotNull FairDiffIterable fragments2) { PeekIterator<Range> unchanged1 = new PeekIterator<>(fragments1.unchanged()); PeekIterator<Range> unchanged2 = new PeekIterator<>(fragments2.unchanged()); while (!unchanged1.atEnd() && !unchanged2.atEnd()) { Side side = add(unchanged1.peek(), unchanged2.peek()); side.select(unchanged1, unchanged2).advance(); } return finish(fragments1, fragments2); } @NotNull private Side add(@NotNull Range range1, @NotNull Range range2) { int start1 = range1.start1; int end1 = range1.end1; int start2 = range2.start1; int end2 = range2.end1; if (end1 <= start2) return Side.LEFT; if (end2 <= start1) return Side.RIGHT; int startBase = Math.max(start1, start2); int endBase = Math.min(end1, end2); int startShift1 = startBase - start1; int endCut1 = end1 - endBase; int startShift2 = startBase - start2; int endCut2 = end2 - endBase; int startLeft = range1.start2 + startShift1; int endLeft = range1.end2 - endCut1; int startRight = range2.start2 + startShift2; int endRight = range2.end2 - endCut2; myChangesBuilder.markEqual(startLeft, startBase, startRight, endLeft, endBase, endRight); return Side.fromLeft(end1 <= end2); } @NotNull private List<MergeRange> finish(@NotNull FairDiffIterable fragments1, @NotNull FairDiffIterable fragments2) { int length1 = fragments1.getLength2(); int length2 = fragments1.getLength1(); int length3 = fragments2.getLength2(); return myChangesBuilder.finish(length1, length2, length3); } } private static class ChangeBuilder { @NotNull private final List<MergeRange> myChanges = new ArrayList<>(); private int myIndex1 = 0; private int myIndex2 = 0; private int myIndex3 = 0; private void addChange(int start1, int start2, int start3, int end1, int end2, int end3) { if (start1 == end1 && start2 == end2 && start3 == end3) return; myChanges.add(new MergeRange(start1, end1, start2, end2, start3, end3)); } public void markEqual(int start1, int start2, int start3, int end1, int end2, int end3) { assert myIndex1 <= start1; assert myIndex2 <= start2; assert myIndex3 <= start3; assert start1 <= end1; assert start2 <= end2; assert start3 <= end3; addChange(myIndex1, myIndex2, myIndex3, start1, start2, start3); myIndex1 = end1; myIndex2 = end2; myIndex3 = end3; } @NotNull public List<MergeRange> finish(int length1, int length2, int length3) { assert myIndex1 <= length1; assert myIndex2 <= length2; assert myIndex3 <= length3; addChange(myIndex1, myIndex2, myIndex3, length1, length2, length3); return myChanges; } } private static class PeekIterator<T> { @NotNull private final Iterator<T> myIterator; private T myValue = null; public PeekIterator(@NotNull Iterator<T> iterator) { myIterator = iterator; advance(); } public boolean atEnd() { return myValue == null; } public boolean hasNext() { return myIterator.hasNext(); } public T peek() { return myValue; } public void advance() { myValue = myIterator.hasNext() ? myIterator.next() : null; } } @Nullable public static CharSequence tryResolveConflict(@NotNull CharSequence leftText, @NotNull CharSequence baseText, @NotNull CharSequence rightText) { if (Registry.is("diff.merge.resolve.conflict.action.use.greedy.approach")) { return MergeResolveUtil.tryGreedyResolve(leftText, baseText, rightText); } else { return MergeResolveUtil.tryResolve(leftText, baseText, rightText); } } }