diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java index 30dab2e70de0..55855a709167 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/Change.java @@ -25,8 +25,10 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Comparator; @@ -47,26 +49,64 @@ public abstract class Change { protected abstract void removeFromList(); + /** + * Called when a change has been applied. + */ + public abstract void onApplied(); + + /** + * Called when a change has been removed from the list. + */ public abstract void onRemovedFromList(); public abstract boolean isValid(); + /** + * Apply the change, i.e. change the "Merge result" document and update range markers, highlighting, gutters, etc. + * @param source The source side of the change, which is being applied. + */ private void apply(@NotNull FragmentSide original) { FragmentSide targetSide = original.otherSide(); RangeMarker originalRangeMarker = getRangeMarker(original); RangeMarker rangeMarker = getRangeMarker(targetSide); if (originalRangeMarker != null && rangeMarker != null) { - apply(getProject(), originalRangeMarker, rangeMarker); - if (isValid()) { - removeFromList(); + TextRange textRange = modifyDocument(getProject(), originalRangeMarker, rangeMarker); + if (textRange != null && isValid()) { + updateTargetRangeMarker(targetSide, textRange); } + onApplied(); } } - private static void apply(@NotNull Project project, @NotNull RangeMarker original, @NotNull RangeMarker target) { + /** + * Updates the target marker of a change after the change has been applied + * to allow highlighting of the document modification which has been performed. + * @param targetFragmentSide The side to be changed. + * @param updatedTextRange New text range to be applied to the side. + */ + protected final void updateTargetRangeMarker(@NotNull FragmentSide targetFragmentSide, @NotNull TextRange updatedTextRange) { + ChangeSide targetSide = getChangeSide(targetFragmentSide); + DiffRangeMarker originalRange = targetSide.getRange(); + DiffRangeMarker updatedRange = new DiffRangeMarker(originalRange.getDocument(), updatedTextRange, null); + changeSide(targetSide, updatedRange); + } + + /** + * Substitutes the specified side of this change to a new side which contains the given range. + * @param sideToChange The side to be changed. + * @param newRange New text range of the new side. + */ + protected abstract void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange); + + /** + * Applies the text from the original marker to the target marker. + * @return the resulting TextRange from the target document, or null if the document if not writable. + */ + @Nullable + private static TextRange modifyDocument(@NotNull Project project, @NotNull RangeMarker original, @NotNull RangeMarker target) { Document document = target.getDocument(); - if (!ReadonlyStatusHandler.ensureDocumentWritable(project, document)) return; + if (!ReadonlyStatusHandler.ensureDocumentWritable(project, document)) { return null; } if (DocumentUtil.isEmpty(original)) { int offset = target.getStartOffset(); document.deleteString(offset, target.getEndOffset()); @@ -78,6 +118,7 @@ public abstract class Change { } else { document.replaceString(startOffset, target.getEndOffset(), text); } + return new TextRange(startOffset, startOffset + text.length()); } public void addMarkup(Editor[] editors) { @@ -152,11 +193,18 @@ public abstract class Change { protected static class SimpleChangeSide extends ChangeSide { private final FragmentSide mySide; private final DiffRangeMarker myRange; - private final ChangeHighlighterHolder myHighlighterHolder = new ChangeHighlighterHolder(); + private final ChangeHighlighterHolder myHighlighterHolder; public SimpleChangeSide(FragmentSide side, DiffRangeMarker rangeMarker) { mySide = side; myRange = rangeMarker; + myHighlighterHolder = new ChangeHighlighterHolder(); + } + + public SimpleChangeSide(ChangeSide originalSide, DiffRangeMarker newRange) { + mySide = ((SimpleChangeSide)originalSide).getFragmentSide(); + myRange = newRange; + myHighlighterHolder = originalSide.getHighlighterHolder(); } public FragmentSide getFragmentSide() { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeCounter.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeCounter.java index 24ecfef705dc..c4e0dbefcee7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeCounter.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeCounter.java @@ -35,6 +35,11 @@ public class ChangeCounter implements ChangeList.Listener { updateCounters(); } + @Override + public void onChangeApplied(ChangeList source) { + updateCounters(); + } + public void onChangeRemoved(ChangeList source) { updateCounters(); } @@ -60,8 +65,7 @@ public class ChangeCounter implements ChangeList.Listener { private void fireCountersChanged() { Listener[] listeners = myListeners.toArray(new Listener[myListeners.size()]); - for (int i = 0; i < listeners.length; i++) { - Listener listener = listeners[i]; + for (Listener listener : listeners) { listener.onCountersChanged(this); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java index 137aac7cc6d6..3c445cc106c8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeHighlighterHolder.java @@ -82,7 +82,7 @@ class ChangeHighlighterHolder { private void setHighlighter(ChangeSide changeSide, ChangeType type) { myMainHighlighter = type.addMarker(changeSide, this); - updateAction(); + updateActions(); } public Editor getEditor() { @@ -112,10 +112,10 @@ class ChangeHighlighterHolder { public void setActions(AnAction[] action) { myActions = action; - updateAction(); + updateActions(); } - private void updateAction() { + private void updateActions() { removeActionHighlighters(); if (myMainHighlighter != null && myActions != null && myActions.length > 0) { myActionHighlighters = new RangeHighlighter[myActions.length]; diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java index ded9f26b3cc1..078bba811638 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeList.java @@ -33,12 +33,13 @@ import java.util.*; public class ChangeList { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.incrementalMerge.ChangeList"); - private static final Comparator CHANGE_ORDER = new SimpleChange.ChangeOrder(FragmentSide.SIDE1); + public static final Comparator CHANGE_ORDER = new SimpleChange.ChangeOrder(FragmentSide.SIDE1); private final Project myProject; private final Document[] myDocuments = new Document[2]; private final ArrayList myListeners = new ArrayList(); private ArrayList myChanges; + private ArrayList myAppliedChanges; public ChangeList(Document base, Document version, Project project) { myDocuments[0] = base; @@ -70,6 +71,7 @@ public class ChangeList { LOG.assertTrue(change.isValid()); } myChanges = new ArrayList(changes); + myAppliedChanges = new ArrayList(); } public Project getProject() { return myProject; } @@ -187,15 +189,28 @@ public class ChangeList { } public LineBlocks getLineBlocks() { - return LineBlocks.fromChanges(myChanges); + ArrayList changes = new ArrayList(myChanges); + changes.addAll(myAppliedChanges); + return LineBlocks.fromChanges(changes); } public void remove(Change change) { - LOG.assertTrue(myChanges.remove(change), change); + if (change.getType().isApplied()) { + LOG.assertTrue(myAppliedChanges.remove(change), change); + } + else { + LOG.assertTrue(myChanges.remove(change), change); + } change.onRemovedFromList(); fireOnChangeRemoved(); } + public void apply(Change change) { + LOG.assertTrue(myChanges.remove(change), change); + myAppliedChanges.add(change); + fireOnChangeApplied(); + } + private void fireOnChangeRemoved() { Listener[] listeners = myListeners.toArray(new Listener[myListeners.size()]); for (Listener listener : listeners) { @@ -203,7 +218,15 @@ public class ChangeList { } } + void fireOnChangeApplied() { + Listener[] listeners = myListeners.toArray(new Listener[myListeners.size()]); + for (Listener listener : listeners) { + listener.onChangeApplied(this); + } + } + public interface Listener { void onChangeRemoved(ChangeList source); + void onChangeApplied(ChangeList source); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java index 38382b003050..d53490ff22b3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ChangeType.java @@ -17,17 +17,11 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.diff.ex.DiffFragment; import com.intellij.openapi.diff.impl.highlighting.LineRenderer; -import com.intellij.openapi.diff.impl.util.DocumentUtil; import com.intellij.openapi.diff.impl.util.TextDiffType; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.editor.markup.HighlighterLayer; -import com.intellij.openapi.editor.markup.HighlighterTargetArea; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.editor.markup.SeparatorPlacement; -import com.intellij.openapi.project.Project; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.ReadonlyStatusHandler; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,44 +30,94 @@ import java.awt.*; public class ChangeType { private static final int LAYER = HighlighterLayer.SELECTION - 1; - private static final ChangeType INSERT = new ChangeType(TextDiffType.INSERT); - private static final ChangeType DELETED = new ChangeType(TextDiffType.DELETED); - private static final ChangeType CHANGE = new ChangeType(TextDiffType.CHANGED); - static final ChangeType CONFLICT = new ChangeType(TextDiffType.CONFLICT); + private static final ChangeType INSERT = new ChangeType(TextDiffType.INSERT, false); + private static final ChangeType DELETED = new ChangeType(TextDiffType.DELETED, false); + private static final ChangeType CHANGE = new ChangeType(TextDiffType.CHANGED, false); + static final ChangeType CONFLICT = new ChangeType(TextDiffType.CONFLICT, false); private final TextDiffType myDiffType; + private final boolean myApplied; - private ChangeType(TextDiffType diffType) { - myDiffType = diffType; + private ChangeType(TextDiffType diffType, boolean applied) { + myApplied = applied; + if (applied) { + myDiffType = TextDiffType.deriveApplied(diffType); + } + else { + myDiffType = diffType; + } } + public boolean isApplied() { + return myApplied; + } + + @NotNull + public static ChangeType deriveApplied(ChangeType type) { + return new ChangeType(type.myDiffType, true); + } + + @Nullable public RangeHighlighter addMarker(ChangeSide changeSide, ChangeHighlighterHolder markup) { String text = changeSide.getText(); - if (text != null && text.length() > 0) return addBlock(text, changeSide, markup, myDiffType); - else return addLine(markup, changeSide.getStartLine(), myDiffType, SeparatorPlacement.TOP); + if (text != null && text.length() > 0) { + return addBlock(text, changeSide, markup, myDiffType); + } + else { + return addLine(markup, changeSide.getStartLine(), myDiffType, SeparatorPlacement.TOP); + } } + @NotNull public TextDiffType getTypeKey() { return myDiffType; } - public TextDiffType getTextDiffType() { return getTypeKey(); } + @NotNull + public TextDiffType getTextDiffType() { + return getTypeKey(); + } + + @Nullable + private RangeHighlighter addBlock(String text, ChangeSide changeSide, ChangeHighlighterHolder markup, TextDiffType diffType) { + EditorColorsScheme colorScheme = markup.getEditor().getColorsScheme(); + Color separatorColor = getSeparatorColor(diffType.getLegendColor(colorScheme)); + LineSeparatorRenderer separatorRenderer = new LineSeparatorRenderer() { + @Override + public void drawLine(Graphics g, int x1, int x2, int y) { + Graphics2D g2 = (Graphics2D) g; + if (myApplied) { + UIUtil.drawBoldDottedLine(g2, x1, x2, y, g2.getBackground(), g2.getColor(), false); + } + else { + UIUtil.drawDottedLine(g2, x1, y, x2, y, g2.getBackground(), g2.getColor()); + } + } + }; - private static RangeHighlighter addBlock(String text, ChangeSide changeSide, ChangeHighlighterHolder markup, TextDiffType diffType) { int length = text.length(); int start = changeSide.getStart(); int end = start + length; - RangeHighlighter highlighter = markup.addRangeHighlighter(start, end, ChangeType.LAYER, diffType, HighlighterTargetArea.EXACT_RANGE); - highlighter.setLineSeparatorPlacement(SeparatorPlacement.TOP); - highlighter.setLineMarkerRenderer(LineRenderer.top()); - highlighter.setLineSeparatorColor(Color.GRAY); + RangeHighlighter highlighter = markup.addRangeHighlighter(start, end, LAYER, diffType, HighlighterTargetArea.EXACT_RANGE); + + if (highlighter != null) { + highlighter.setLineSeparatorPlacement(SeparatorPlacement.TOP); + highlighter.setLineSeparatorColor(separatorColor); + highlighter.setLineSeparatorRenderer(separatorRenderer); + highlighter.setLineMarkerRenderer(LineRenderer.top()); + } + if (text.charAt(length - 1) == '\n') { end--; } + highlighter = markup.addRangeHighlighter(start, end, LAYER, TextDiffType.NONE, HighlighterTargetArea.EXACT_RANGE); - highlighter.setLineSeparatorPlacement(SeparatorPlacement.BOTTOM); - highlighter.setLineSeparatorColor(Color.GRAY); - highlighter.setLineMarkerRenderer(LineRenderer.bottom()); + if (highlighter != null) { + highlighter.setLineSeparatorPlacement(SeparatorPlacement.BOTTOM); + highlighter.setLineSeparatorColor(separatorColor); + highlighter.setLineSeparatorRenderer(separatorRenderer); + highlighter.setLineMarkerRenderer(LineRenderer.bottom()); + } return highlighter; } @@ -103,4 +147,13 @@ public class ChangeType { return myDiffType.getDisplayName(); } + @NotNull + public Color getSeparatorColor(@Nullable Color highlightColor) { + if (myApplied) { + return highlightColor == null ? Color.DARK_GRAY : highlightColor.darker(); + } + return Color.GRAY; + } + + } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java index c6820c91dfbe..f43adc6c0fd1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.diff.impl.incrementalMerge; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.util.TextRange; @@ -25,12 +27,24 @@ import com.intellij.openapi.util.TextRange; */ class ConflictChange extends Change implements DiffRangeMarker.RangeInvalidListener { + private static final Logger LOG = Logger.getInstance(ConflictChange.class); + private SimpleChangeSide myOriginalSide; private MergeConflict myConflict; + private final ChangeList myChangeList; + private ChangeType myType; + private boolean mySemiApplied; - public ConflictChange(MergeConflict conflict, FragmentSide mergeSide, TextRange range) { + public ConflictChange(MergeConflict conflict, FragmentSide mergeSide, TextRange range, ChangeList changeList) { myConflict = conflict; + myChangeList = changeList; myOriginalSide = new SimpleChangeSide(mergeSide, new DiffRangeMarker((DocumentEx)conflict.getOriginalDocument(mergeSide), range, this)); + myType = ChangeType.CONFLICT; + } + + @Override + protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { + myConflict.setRange(newRange); } protected void removeFromList() { @@ -51,13 +65,43 @@ class ConflictChange extends Change implements DiffRangeMarker.RangeInvalidListe } public ChangeType getType() { - return ChangeType.CONFLICT; + return myType; } public ChangeList getChangeList() { return myConflict.getMergeList().getChanges(myOriginalSide.getFragmentSide()); } + @Override + public void onApplied() { + myType = ChangeType.deriveApplied(myType); + myChangeList.apply(this); + + myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); + myOriginalSide.getHighlighterHolder().setActions(new AnAction[0]); + + // display, what one side of the conflict was resolved to + myConflict.getHighlighterHolder().updateHighlighter(myConflict, myType); + + // update the other variant of the conflict to point to the bottom + if (!mySemiApplied) { + ConflictChange otherChange = myConflict.getOtherChange(this); + LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myConflict)); + otherChange.mySemiApplied = true; + otherChange.updateOtherSideOnConflictApply(); + myConflict.removeOtherChange(this); + } + } + + private void updateOtherSideOnConflictApply() { + int startOffset = myConflict.getRange().getEndOffset(); + TextRange emptyRange = new TextRange(startOffset, startOffset); + myConflict = myConflict.deriveSideForNotAppliedChange(emptyRange, null, this); + myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); + myConflict.getHighlighterHolder().updateHighlighter(myConflict, myType); + myChangeList.fireOnChangeApplied(); + } + public void onRemovedFromList() { myOriginalSide.getRange().removeListener(this); myConflict = null; diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java index ca18b0183b18..728888c8167c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java @@ -20,6 +20,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents a merge conflict, i.e. two {@link ConflictChange conflicting changes}, one from left, another from right. @@ -27,16 +28,33 @@ import org.jetbrains.annotations.NotNull; class MergeConflict extends ChangeSide implements DiffRangeMarker.RangeInvalidListener { @NotNull private final MergeList myMergeList; - @NotNull private final DiffRangeMarker myCommonRange; - @NotNull private final ConflictChange myLeftChange; - @NotNull private final ConflictChange myRightChange; - @NotNull private final ChangeHighlighterHolder myCommonHighlighterHolder = new ChangeHighlighterHolder(); + @NotNull private DiffRangeMarker myCommonRange; + @Nullable private ConflictChange myLeftChange; + @Nullable private ConflictChange myRightChange; + @NotNull private final ChangeHighlighterHolder myCommonHighlighterHolder; MergeConflict(TextRange commonRange, MergeList mergeList, TextRange leftMarker, TextRange rightMarker) { myCommonRange = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(),commonRange, this); myMergeList = mergeList; - myLeftChange = new ConflictChange(this, FragmentSide.SIDE1, leftMarker); - myRightChange = new ConflictChange(this, FragmentSide.SIDE2, rightMarker); + myCommonHighlighterHolder = new ChangeHighlighterHolder(); + myLeftChange = new ConflictChange(this, FragmentSide.SIDE1, leftMarker, mergeList.getLeftChangeList()); + myRightChange = new ConflictChange(this, FragmentSide.SIDE2, rightMarker, mergeList.getRightChangeList()); + } + + private MergeConflict(TextRange commonRange, MergeList mergeList, ChangeHighlighterHolder highlighterHolder, + ConflictChange leftChange, ConflictChange rightChange) { + myCommonRange = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(),commonRange, this); + myMergeList = mergeList; + myCommonHighlighterHolder = highlighterHolder; + myLeftChange = leftChange; + myRightChange = rightChange; + } + + public MergeConflict deriveSideForNotAppliedChange(TextRange baseRange, @Nullable ConflictChange originalChange, ConflictChange otherChange) { + ChangeHighlighterHolder highlighterHolder = new ChangeHighlighterHolder(); + MergeConflict mergeConflict = new MergeConflict(baseRange, myMergeList, highlighterHolder, originalChange, otherChange); + highlighterHolder.highlight(mergeConflict, myCommonHighlighterHolder.getEditor(), ChangeType.CONFLICT); + return mergeConflict; } public ChangeHighlighterHolder getHighlighterHolder() { @@ -47,16 +65,29 @@ class MergeConflict extends ChangeSide implements DiffRangeMarker.RangeInvalidLi return myCommonRange; } - @NotNull + @Nullable public ConflictChange getLeftChange() { return myLeftChange; } - @NotNull + @Nullable public ConflictChange getRightChange() { return myRightChange; } + @Nullable + ConflictChange getOtherChange(ConflictChange change) { + if (change == myLeftChange) { + return myRightChange; + } + else if (change == myRightChange) { + return myLeftChange; + } + else { + throw new IllegalStateException("Unexpected change: " + change); + } + } + public void conflictRemoved() { removeHighlighters(myLeftChange); removeHighlighters(myRightChange); @@ -65,8 +96,26 @@ class MergeConflict extends ChangeSide implements DiffRangeMarker.RangeInvalidLi myCommonRange.removeListener(this); } - private static void removeHighlighters(@NotNull ConflictChange change) { - change.getOriginalSide().getHighlighterHolder().removeHighlighters(); + public void setRange(DiffRangeMarker range) { + myCommonRange = range; + } + + public void removeOtherChange(ConflictChange change) { + if (change == myLeftChange) { + myRightChange = null; + } + else if (change == myRightChange) { + myLeftChange = null; + } + else { + throw new IllegalStateException("Unexpected change: " + change); + } + } + + private static void removeHighlighters(@Nullable ConflictChange change) { + if (change != null) { + change.getOriginalSide().getHighlighterHolder().removeHighlighters(); + } } public Document getOriginalDocument(FragmentSide mergeSide) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java index 95c7fd938aa7..979c76f7cb1e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java @@ -67,6 +67,16 @@ public class MergeList implements UserDataHolder { myBaseToRightChangeList = new ChangeList(base, right, project); } + @NotNull + public ChangeList getLeftChangeList() { + return myBaseToLeftChangeList; + } + + @NotNull + public ChangeList getRightChangeList() { + return myBaseToRightChangeList; + } + public static MergeList create(@NotNull Project project, @NotNull Document left, @NotNull Document base, @NotNull Document right) throws FilesTooBigForDiffException { MergeList mergeList = new MergeList(project, left, base, right); @@ -109,6 +119,8 @@ public class MergeList implements UserDataHolder { } else { MergeConflict conflict = new MergeConflict(baseRange, mergeList, leftRange, rightRange); + assert conflict.getLeftChange() != null; + assert conflict.getRightChange() != null; leftChanges.add(conflict.getLeftChange()); rightChanges.add(conflict.getRightChange()); } @@ -224,9 +236,13 @@ public class MergeList implements UserDataHolder { } } - public void removeChanges(@NotNull Change leftChange, @NotNull Change rightChange) { - myBaseToLeftChangeList.remove(leftChange); - myBaseToRightChangeList.remove(rightChange); + public void removeChanges(@Nullable Change leftChange, @Nullable Change rightChange) { + if (leftChange != null) { + myBaseToLeftChangeList.remove(leftChange); + } + if (rightChange != null) { + myBaseToRightChangeList.remove(rightChange); + } } public Document getBaseDocument() { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java index e0736c3bb373..7432f1438975 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/SimpleChange.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.diff.impl.incrementalMerge; +import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.editor.ex.DocumentEx; @@ -23,7 +24,7 @@ import org.jetbrains.annotations.NotNull; class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListener{ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.incrementalMerge.Change"); - private final ChangeType myType; + private ChangeType myType; private final SimpleChangeSide[] mySides; private final ChangeList myChangeList; @@ -38,6 +39,22 @@ class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListene return new SimpleChangeSide(side, new DiffRangeMarker((DocumentEx)changeList.getDocument(side), range1, this)); } + /** + * Changes the given Side of a Change to a new text range. + * @param sideToChange Side to be changed. + * @param newRange New change range. + */ + @Override + protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { + for (int i = 0; i < mySides.length; i++) { + SimpleChangeSide side = mySides[i]; + if (side.equals(sideToChange)) { + mySides[i] = new SimpleChangeSide(sideToChange, newRange); + break; + } + } + } + protected void removeFromList() { myChangeList.remove(this); } @@ -54,6 +71,17 @@ class SimpleChange extends Change implements DiffRangeMarker.RangeInvalidListene return myChangeList; } + @Override + public void onApplied() { + myType = ChangeType.deriveApplied(myType); + for (SimpleChangeSide side : mySides) { + ChangeHighlighterHolder highlighterHolder = side.getHighlighterHolder(); + highlighterHolder.setActions(new AnAction[0]); + highlighterHolder.updateHighlighter(side, myType); + } + myChangeList.apply(this); + } + public void onRemovedFromList() { for (int i = 0; i < mySides.length; i++) { SimpleChangeSide side = mySides[i]; diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 37bcf703b30e..03d95b5b698c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -497,6 +497,13 @@ public class MergePanel2 implements DiffViewer { } private class DividersRepainter implements ChangeList.Listener { + + @Override + public void onChangeApplied(ChangeList source) { + FragmentSide side = myMergeList.getSideOf(source); + myDividers[side.getIndex()].repaint(); + } + public void onChangeRemoved(ChangeList source) { FragmentSide side = myMergeList.getSideOf(source); myDividers[side.getIndex()].repaint(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java index e5cf315e8e87..023ec4e9aaf2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/DividerPolygon.java @@ -18,7 +18,6 @@ package com.intellij.openapi.diff.impl.splitter; import com.intellij.openapi.diff.impl.EditingSides; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.util.TextDiffType; -import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.util.Comparing; @@ -99,10 +98,7 @@ public class DividerPolygon { ArrayList polygons = new ArrayList(); for (int i = indices.getStart(); i < indices.getEnd(); i++) { Trapezium trapezium = lineBlocks.getTrapezium(i); - final TextDiffTypeEnum diffTypeEnum = lineBlocks.getType(i); - if (diffTypeEnum == null) continue; - TextDiffType type = TextDiffType.create(diffTypeEnum); - if (type == null) continue; + final TextDiffType type = lineBlocks.getType(i); Color color = type.getPolygonColor(editor1); polygons.add(createPolygon(transformations, trapezium, color, left)); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java index 728bd8c55c2f..3bb4c4f44104 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java @@ -20,21 +20,20 @@ import com.intellij.openapi.diff.impl.fragments.LineBlock; import com.intellij.openapi.diff.impl.fragments.LineFragment; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.incrementalMerge.Change; -import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum; +import com.intellij.openapi.diff.impl.incrementalMerge.ChangeList; +import com.intellij.openapi.diff.impl.util.TextDiffType; import com.intellij.util.containers.IntArrayList; +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Iterator; +import java.util.*; public class LineBlocks { - public static final LineBlocks EMPTY = new LineBlocks(SimpleIntervalProvider.EMPTY, new TextDiffTypeEnum[0]); + public static final LineBlocks EMPTY = new LineBlocks(SimpleIntervalProvider.EMPTY, new TextDiffType[0]); private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.splitter.LineBlocks"); private final IntervalsProvider myIntervalsProvider; - private final TextDiffTypeEnum[] myTypes; + private final TextDiffType[] myTypes; - private LineBlocks(IntervalsProvider intervalsProvider, TextDiffTypeEnum[] types) { + private LineBlocks(IntervalsProvider intervalsProvider, TextDiffType[] types) { myIntervalsProvider = intervalsProvider; myTypes = types; } @@ -52,11 +51,11 @@ public class LineBlocks { } public Trapezium getTrapezium(int index) { - return new Trapezium(getIntervals(FragmentSide.SIDE1)[index], + return new Trapezium(getIntervals(FragmentSide.SIDE1)[index], getIntervals(FragmentSide.SIDE2)[index]); } - public TextDiffTypeEnum getType(int index) { + public TextDiffType getType(int index) { return myTypes[index]; } @@ -68,8 +67,8 @@ public class LineBlocks { IntArrayList result = new IntArrayList(getIntervals(side).length); int previousBeginning = Integer.MIN_VALUE; Interval[] sideIntervals = getIntervals(side); - for (int i = 0; i < sideIntervals.length; i++) { - int start = sideIntervals[i].getStart(); + for (Interval sideInterval : sideIntervals) { + int start = sideInterval.getStart(); if (start != previousBeginning) result.add(start); previousBeginning = start; } @@ -140,8 +139,7 @@ public class LineBlocks { public static LineBlocks fromLineFragments(ArrayList lines) { ArrayList filtered = new ArrayList(); - for (Iterator iterator = lines.iterator(); iterator.hasNext();) { - LineFragment fragment = iterator.next(); + for (LineFragment fragment : lines) { if (fragment.getType() != null) filtered.add(fragment); } return createLineBlocks(filtered.toArray(new LineBlock[filtered.size()])); @@ -151,56 +149,45 @@ public class LineBlocks { Arrays.sort(blocks, LineBlock.COMPARATOR); Interval[] intervals1 = new Interval[blocks.length]; Interval[] intervals2 = new Interval[blocks.length]; - TextDiffTypeEnum[] types = new TextDiffTypeEnum[blocks.length]; + TextDiffType[] types = new TextDiffType[blocks.length]; for (int i = 0; i < blocks.length; i++) { LineBlock block = blocks[i]; intervals1[i] = new Interval(block.getStartingLine1(), block.getModifiedLines1()); intervals2[i] = new Interval(block.getStartingLine2(), block.getModifiedLines2()); - types[i] = block.getType(); + types[i] = TextDiffType.create(block.getType()); } return create(intervals1, intervals2, types); } - private static LineBlocks create(Interval[] intervals1, Interval[] intervals2, TextDiffTypeEnum[] types) { + private static LineBlocks create(Interval[] intervals1, Interval[] intervals2, TextDiffType[] types) { return new LineBlocks(new SimpleIntervalProvider(intervals1, intervals2), types); } - public static LineBlocks fromChanges(ArrayList changes) { + @NotNull + public static LineBlocks fromChanges(@NotNull List changes) { + // changes may come mixed, need to sort them to get correct intervals + Collections.sort(changes, ChangeList.CHANGE_ORDER); + ArrayList intervals1 = new ArrayList(); ArrayList intervals2 = new ArrayList(); - ArrayList types = new ArrayList(); - //int prevEnd1 = 0; - //int prevEnd2 = 0; - for (Iterator iterator = changes.iterator(); iterator.hasNext();) { - Change change = iterator.next(); - if (!change.isValid()) continue; + ArrayList types = new ArrayList(); + for (Change change : changes) { + if (!change.isValid()) { continue; } int start1 = change.getChangeSide(FragmentSide.SIDE1).getStartLine(); - int start2 = change.getChangeSide(FragmentSide.SIDE2).getStartLine(); - //if (start1 != prevEnd1 || start2 != prevEnd2) { - // intervals1.add(Interval.fromTo(prevEnd1, start1)); - // intervals2.add(Interval.fromTo(prevEnd2, start2)); - // types.add(null); - //} int end1 = change.getChangeSide(FragmentSide.SIDE1).getEndLine(); intervals1.add(Interval.fromTo(start1, end1)); + + int start2 = change.getChangeSide(FragmentSide.SIDE2).getStartLine(); int end2 = change.getChangeSide(FragmentSide.SIDE2).getEndLine(); intervals2.add(Interval.fromTo(start2, end2)); - types.add(change.getType().getTypeKey().getType()); - //prevEnd1 = end1; - //prevEnd2 = end2; - } - //LOG.assertTrue(prevEnd1 < length1 && prevEnd2 < length2); - //if (prevEnd1 != length1 || prevEnd2 != length2) { - // intervals1.add(Interval.fromTo(prevEnd1, length1)); - // intervals2.add(Interval.fromTo(prevEnd2, length2)); - // types.add(null); - //} + types.add(change.getType().getTypeKey()); + } return create(intervals1.toArray(new Interval[intervals1.size()]), - intervals2.toArray(new Interval[intervals2.size()]), types.toArray(new TextDiffTypeEnum[types.size()])); + intervals2.toArray(new Interval[intervals2.size()]), types.toArray(new TextDiffType[types.size()])); } - public TextDiffTypeEnum[] getTypes() { + public TextDiffType[] getTypes() { return myTypes; } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java index f713c7e5153d..5c7595d8ae60 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/TextDiffType.java @@ -38,17 +38,20 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { public static final TextDiffType NONE = new TextDiffType(TextDiffTypeEnum.NONE, DiffBundle.message("diff.type.none.name"), null); - private final TextDiffTypeEnum myType; public static final List DIFF_TYPES = Arrays.asList(DELETED, CHANGED, INSERT); public static final List MERGE_TYPES = Arrays.asList(DELETED, CHANGED, INSERT, CONFLICT); - private final TextAttributesKey myAttributesKey; - private final String myDisplayName; + public static final Convertor ATTRIBUTES_KEY = new Convertor() { public TextAttributesKey convert(TextDiffType textDiffType) { return textDiffType.getAttributesKey(); } }; + private final TextDiffTypeEnum myType; + private final TextAttributesKey myAttributesKey; + private final String myDisplayName; + private final boolean myApplied; + public static TextDiffType create(@NotNull final TextDiffTypeEnum type) { if (TextDiffTypeEnum.INSERT.equals(type)) { return INSERT; @@ -59,15 +62,29 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { } else if (TextDiffTypeEnum.CONFLICT.equals(type)) { return CONFLICT; } else { - // NONE return NONE; } } - private TextDiffType(TextDiffTypeEnum type, String displayName, TextAttributesKey attrubutesKey) { + /** + * Creates a new TextDiffType based on the given one. + * @param source + * @return + */ + @NotNull + public static TextDiffType deriveApplied(@NotNull TextDiffType source) { + return new TextDiffType(source.myType, source.myDisplayName, source.myAttributesKey, true); + } + + private TextDiffType(TextDiffTypeEnum type, String displayName, TextAttributesKey attributesKey) { + this(type, displayName, attributesKey, false); + } + + private TextDiffType(TextDiffTypeEnum type, String displayName, TextAttributesKey attributesKey, boolean applied) { myType = type; - myAttributesKey = attrubutesKey; + myAttributesKey = attributesKey; myDisplayName = displayName; + myApplied = applied; } public String getDisplayName() { @@ -76,7 +93,7 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { @Nullable public Color getLegendColor(EditorColorsScheme colorScheme) { - TextAttributes attributes = getTextAttributes(colorScheme); + TextAttributes attributes = colorScheme.getAttributes(myAttributesKey); return attributes != null ? attributes.getBackgroundColor() : null; } @@ -85,7 +102,15 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { } public TextAttributes getTextAttributes(EditorColorsScheme scheme) { - return scheme.getAttributes(myAttributesKey); + TextAttributes originalAttrs = scheme.getAttributes(myAttributesKey); + if (!myApplied) { + return originalAttrs; + } + else { + TextAttributes overridingAttributes = new TextAttributes(); + overridingAttributes.setBackgroundColor(scheme.getDefaultBackground()); + return TextAttributes.merge(originalAttrs, overridingAttributes); + } } @Nullable @@ -104,7 +129,7 @@ public class TextDiffType implements DiffStatusBar.LegendTypeDescriptor { } public String toString(){ - return myDisplayName; + return myApplied ? myDisplayName + "_applied" : myDisplayName; } public TextDiffTypeEnum getType() {