diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java index a33927700ad6..61f9a8e1cb02 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java @@ -15,170 +15,44 @@ */ package com.intellij.diff.tools.simple; -import com.intellij.diff.fragments.DiffFragment; import com.intellij.diff.fragments.LineFragment; -import com.intellij.diff.util.*; -import com.intellij.openapi.actionSystem.Shortcut; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.markup.GutterIconRenderer; -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.keymap.KeymapManager; -import com.intellij.openapi.keymap.KeymapUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.diff.util.DiffUtil; +import com.intellij.diff.util.Side; +import com.intellij.diff.util.TextDiffType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.List; public class SimpleDiffChange { - @NotNull protected final SimpleDiffViewer myViewer; + private final int myIndex; @NotNull private final LineFragment myFragment; - @Nullable private final List myInnerFragments; private final boolean myIsExcluded; private final boolean myIsSkipped; - @NotNull protected final List myHighlighters = new ArrayList<>(); - @NotNull protected final List myOperations = new ArrayList<>(); - private boolean myIsValid = true; + private boolean myIsDestroyed = true; private final int[] myLineStartShifts = new int[2]; private final int[] myLineEndShifts = new int[2]; - public SimpleDiffChange(@NotNull SimpleDiffViewer viewer, + public SimpleDiffChange(int index, @NotNull LineFragment fragment) { - this(viewer, fragment, false, false); + this(index, fragment, false, false); } - public SimpleDiffChange(@NotNull SimpleDiffViewer viewer, + public SimpleDiffChange(int index, @NotNull LineFragment fragment, boolean isExcluded, boolean isSkipped) { - myViewer = viewer; + myIndex = index; myFragment = fragment; - myInnerFragments = fragment.getInnerFragments(); myIsExcluded = isExcluded; myIsSkipped = isSkipped; } - public void installHighlighter(@Nullable LineFragment previousFragment) { - assert myHighlighters.isEmpty(); - - if (myInnerFragments != null) { - doInstallHighlighterWithInner(); - } - else { - doInstallHighlighterSimple(); - } - doInstallNonSquashedChangesSeparator(previousFragment); - - doInstallActionHighlighters(); + public int getIndex() { + return myIndex; } - public void destroyHighlighter() { - for (RangeHighlighter highlighter : myHighlighters) { - highlighter.dispose(); - } - myHighlighters.clear(); - - for (GutterOperation operation : myOperations) { - operation.dispose(); - } - myOperations.clear(); - } - - private void doInstallHighlighterSimple() { - createHighlighter(Side.LEFT, false); - createHighlighter(Side.RIGHT, false); - } - - private void doInstallHighlighterWithInner() { - assert myInnerFragments != null; - - createHighlighter(Side.LEFT, true); - createHighlighter(Side.RIGHT, true); - - for (DiffFragment fragment : myInnerFragments) { - createInlineHighlighter(fragment, Side.LEFT); - createInlineHighlighter(fragment, Side.RIGHT); - } - } - - private void doInstallNonSquashedChangesSeparator(@Nullable LineFragment previousFragment) { - createNonSquashedChangesSeparator(previousFragment, Side.LEFT); - createNonSquashedChangesSeparator(previousFragment, Side.RIGHT); - } - - protected void doInstallActionHighlighters() { - if (myIsSkipped) return; - - myOperations.add(new AcceptGutterOperation(Side.LEFT)); - myOperations.add(new AcceptGutterOperation(Side.RIGHT)); - } - - private void createHighlighter(@NotNull Side side, boolean ignored) { - Editor editor = myViewer.getEditor(side); - - TextDiffType type = DiffUtil.getLineDiffType(myFragment); - int startLine = side.getStartLine(myFragment); - int endLine = side.getEndLine(myFragment); - - myHighlighters.addAll(new DiffDrawUtil.LineHighlighterBuilder(editor, startLine, endLine, type) - .withIgnored(ignored) - .withExcludedInEditor(myIsSkipped) - .withExcludedInGutter(myIsExcluded) - .done()); - } - - private void createInlineHighlighter(@NotNull DiffFragment fragment, @NotNull Side side) { - if (myIsSkipped) return; - - int start = side.getStartOffset(fragment); - int end = side.getEndOffset(fragment); - TextDiffType type = DiffUtil.getDiffType(fragment); - - int startOffset = side.getStartOffset(myFragment); - start += startOffset; - end += startOffset; - - Editor editor = myViewer.getEditor(side); - myHighlighters.addAll(DiffDrawUtil.createInlineHighlighter(editor, start, end, type)); - } - - private void createNonSquashedChangesSeparator(@Nullable LineFragment previousFragment, @NotNull Side side) { - if (previousFragment == null) return; - - int startLine = side.getStartLine(myFragment); - int endLine = side.getEndLine(myFragment); - - int prevStartLine = side.getStartLine(previousFragment); - int prevEndLine = side.getEndLine(previousFragment); - - if (startLine == endLine) return; - if (prevStartLine == prevEndLine) return; - if (prevEndLine != startLine) return; - - myHighlighters.addAll(DiffDrawUtil.createLineMarker(myViewer.getEditor(side), startLine, TextDiffType.MODIFIED)); - } - - public void updateGutterActions(boolean force) { - for (GutterOperation operation : myOperations) { - operation.update(force); - } - } - - // - // Getters - // - public int getStartLine(@NotNull Side side) { return side.getStartLine(myFragment) + side.select(myLineStartShifts); } @@ -204,16 +78,16 @@ public class SimpleDiffChange { return myIsValid; } + public boolean isDestroyed() { + return myIsDestroyed; + } + @NotNull public LineFragment getFragment() { return myFragment; } - // - // Shift - // - - public boolean processChange(int oldLine1, int oldLine2, int shift, @NotNull Side side) { + public boolean processDocumentChange(int oldLine1, int oldLine2, int shift, @NotNull Side side) { int line1 = getStartLine(side); int line2 = getEndLine(side); int sideIndex = side.getIndex(); @@ -223,124 +97,13 @@ public class SimpleDiffChange { myLineEndShifts[sideIndex] += newRange.endLine - line2; if (newRange.damaged) { - for (GutterOperation operation : myOperations) { - operation.dispose(); - } - myOperations.clear(); - myIsValid = false; } return newRange.damaged; } - // - // Helpers - // - - protected abstract class GutterOperation { - @NotNull protected final Side mySide; - @NotNull private final RangeHighlighter myHighlighter; - - protected boolean myCtrlPressed; - - public GutterOperation(@NotNull Side side) { - mySide = side; - - int offset = side.getStartOffset(myFragment); - EditorEx editor = myViewer.getEditor(side); - myHighlighter = editor.getMarkupModel().addRangeHighlighter(offset, offset, - HighlighterLayer.ADDITIONAL_SYNTAX, - null, - HighlighterTargetArea.LINES_IN_RANGE); - - update(true); - } - - public void dispose() { - myHighlighter.dispose(); - } - - public void update(boolean force) { - if (!force && !areModifiersChanged()) { - return; - } - if (myHighlighter.isValid()) myHighlighter.setGutterIconRenderer(createRenderer()); - } - - private boolean areModifiersChanged() { - return myCtrlPressed != myViewer.getModifierProvider().isCtrlPressed(); - } - - @Nullable - public abstract GutterIconRenderer createRenderer(); - } - - private class AcceptGutterOperation extends GutterOperation { - AcceptGutterOperation(@NotNull Side side) { - super(side); - } - - @Nullable - @Override - public GutterIconRenderer createRenderer() { - myCtrlPressed = myViewer.getModifierProvider().isCtrlPressed(); - - boolean isOtherEditable = DiffUtil.isEditable(myViewer.getEditor(mySide.other())); - boolean isAppendable = myFragment.getStartLine1() != myFragment.getEndLine1() && - myFragment.getStartLine2() != myFragment.getEndLine2(); - - if (isOtherEditable) { - if (myCtrlPressed && isAppendable) { - return createAppendRenderer(mySide); - } - else { - return createApplyRenderer(mySide); - } - } - return null; - } - } - - @Nullable - private GutterIconRenderer createApplyRenderer(@NotNull final Side side) { - String text; - Icon icon = DiffUtil.getArrowIcon(side); - - if (side == Side.LEFT && myViewer.isDiffForLocalChanges()) { - text = "Revert"; - } - else { - text = "Accept"; - } - - String actionId = side.select("Diff.ApplyLeftSide", "Diff.ApplyRightSide"); - Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(actionId); - String shortcutsText = StringUtil.nullize(KeymapUtil.getShortcutsText(shortcuts)); - String tooltipText = DiffUtil.createTooltipText(text, shortcutsText); - - return createIconRenderer(side, tooltipText, icon, () -> myViewer.replaceChange(this, side)); - } - - @Nullable - private GutterIconRenderer createAppendRenderer(@NotNull final Side side) { - return createIconRenderer(side, "Append", DiffUtil.getArrowDownIcon(side), () -> myViewer.appendChange(this, side)); - } - - @Nullable - private GutterIconRenderer createIconRenderer(@NotNull final Side sourceSide, - @NotNull final String tooltipText, - @NotNull final Icon icon, - @NotNull final Runnable perform) { - if (!DiffUtil.isEditable(myViewer.getEditor(sourceSide.other()))) return null; - return new DiffGutterRenderer(icon, tooltipText) { - @Override - protected void handleMouseClick() { - if (!myIsValid) return; - final Project project = myViewer.getProject(); - final Document document = myViewer.getEditor(sourceSide.other()).getDocument(); - DiffUtil.executeWriteCommand(document, project, "Replace change", perform); - } - }; + public void markDestroyed() { + myIsDestroyed = true; } } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChangeUi.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChangeUi.java new file mode 100644 index 000000000000..6b5ac9deb38a --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChangeUi.java @@ -0,0 +1,257 @@ +// Copyright 2000-2019 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.tools.simple; + +import com.intellij.diff.fragments.DiffFragment; +import com.intellij.diff.util.*; +import com.intellij.openapi.actionSystem.Shortcut; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.markup.GutterIconRenderer; +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.keymap.KeymapManager; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class SimpleDiffChangeUi { + @NotNull protected final SimpleDiffViewer myViewer; + @NotNull protected final SimpleDiffChange myChange; + + @NotNull protected final List myHighlighters = new ArrayList<>(); + @NotNull protected final List myOperations = new ArrayList<>(); + + public SimpleDiffChangeUi(@NotNull SimpleDiffViewer viewer, @NotNull SimpleDiffChange change) { + myViewer = viewer; + myChange = change; + } + + public void installHighlighter(@Nullable SimpleDiffChange previousChange) { + assert myHighlighters.isEmpty(); + + List innerFragments = myChange.getFragment().getInnerFragments(); + if (innerFragments != null) { + doInstallHighlighterWithInner(innerFragments); + } + else { + doInstallHighlighterSimple(); + } + doInstallNonSquashedChangesSeparator(previousChange); + + doInstallActionHighlighters(); + } + + public void destroyHighlighter() { + for (RangeHighlighter highlighter : myHighlighters) { + highlighter.dispose(); + } + myHighlighters.clear(); + + for (GutterOperation operation : myOperations) { + operation.dispose(); + } + myOperations.clear(); + } + + private void doInstallHighlighterSimple() { + createHighlighter(Side.LEFT, false); + createHighlighter(Side.RIGHT, false); + } + + private void doInstallHighlighterWithInner(@NotNull List innerFragments) { + createHighlighter(Side.LEFT, true); + createHighlighter(Side.RIGHT, true); + + for (DiffFragment fragment : innerFragments) { + createInlineHighlighter(fragment, Side.LEFT); + createInlineHighlighter(fragment, Side.RIGHT); + } + } + + private void doInstallNonSquashedChangesSeparator(@Nullable SimpleDiffChange previousChange) { + createNonSquashedChangesSeparator(previousChange, Side.LEFT); + createNonSquashedChangesSeparator(previousChange, Side.RIGHT); + } + + protected void doInstallActionHighlighters() { + if (myChange.isSkipped()) return; + + myOperations.add(new AcceptGutterOperation(Side.LEFT)); + myOperations.add(new AcceptGutterOperation(Side.RIGHT)); + } + + private void createHighlighter(@NotNull Side side, boolean ignored) { + Editor editor = myViewer.getEditor(side); + + TextDiffType type = myChange.getDiffType(); + int startLine = myChange.getStartLine(side); + int endLine = myChange.getEndLine(side); + + myHighlighters.addAll(new DiffDrawUtil.LineHighlighterBuilder(editor, startLine, endLine, type) + .withIgnored(ignored) + .withExcludedInEditor(myChange.isSkipped()) + .withExcludedInGutter(myChange.isExcluded()) + .done()); + } + + private void createInlineHighlighter(@NotNull DiffFragment innerFragment, @NotNull Side side) { + if (myChange.isSkipped()) return; + + int start = side.getStartOffset(innerFragment); + int end = side.getEndOffset(innerFragment); + TextDiffType type = DiffUtil.getDiffType(innerFragment); + + int startOffset = side.getStartOffset(myChange.getFragment()); + start += startOffset; + end += startOffset; + + Editor editor = myViewer.getEditor(side); + myHighlighters.addAll(DiffDrawUtil.createInlineHighlighter(editor, start, end, type)); + } + + private void createNonSquashedChangesSeparator(@Nullable SimpleDiffChange previousChange, @NotNull Side side) { + if (previousChange == null) return; + + int startLine = myChange.getStartLine(side); + int endLine = myChange.getEndLine(side); + + int prevStartLine = previousChange.getStartLine(side); + int prevEndLine = previousChange.getEndLine(side); + + if (startLine == endLine) return; + if (prevStartLine == prevEndLine) return; + if (prevEndLine != startLine) return; + + myHighlighters.addAll(DiffDrawUtil.createLineMarker(myViewer.getEditor(side), startLine, TextDiffType.MODIFIED)); + } + + public void updateGutterActions(boolean force) { + for (GutterOperation operation : myOperations) { + operation.update(force); + } + } + + public void invalidate() { + for (GutterOperation operation : myOperations) { + operation.dispose(); + } + myOperations.clear(); + } + + // + // Helpers + // + + protected abstract class GutterOperation { + @NotNull protected final Side mySide; + @NotNull private final RangeHighlighter myHighlighter; + + protected boolean myCtrlPressed; + + public GutterOperation(@NotNull Side side) { + mySide = side; + + int offset = side.getStartOffset(myChange.getFragment()); + EditorEx editor = myViewer.getEditor(side); + myHighlighter = editor.getMarkupModel().addRangeHighlighter(offset, offset, + HighlighterLayer.ADDITIONAL_SYNTAX, + null, + HighlighterTargetArea.LINES_IN_RANGE); + + update(true); + } + + public void dispose() { + myHighlighter.dispose(); + } + + public void update(boolean force) { + if (!force && !areModifiersChanged()) { + return; + } + if (myHighlighter.isValid()) myHighlighter.setGutterIconRenderer(createRenderer()); + } + + private boolean areModifiersChanged() { + return myCtrlPressed != myViewer.getModifierProvider().isCtrlPressed(); + } + + @Nullable + public abstract GutterIconRenderer createRenderer(); + } + + private class AcceptGutterOperation extends GutterOperation { + AcceptGutterOperation(@NotNull Side side) { + super(side); + } + + @Nullable + @Override + public GutterIconRenderer createRenderer() { + myCtrlPressed = myViewer.getModifierProvider().isCtrlPressed(); + + boolean isOtherEditable = DiffUtil.isEditable(myViewer.getEditor(mySide.other())); + boolean isAppendable = myChange.getDiffType() == TextDiffType.MODIFIED; + + if (isOtherEditable) { + if (myCtrlPressed && isAppendable) { + return createAppendRenderer(mySide); + } + else { + return createApplyRenderer(mySide); + } + } + return null; + } + } + + @Nullable + private GutterIconRenderer createApplyRenderer(@NotNull final Side side) { + String text; + Icon icon = DiffUtil.getArrowIcon(side); + + if (side == Side.LEFT && myViewer.isDiffForLocalChanges()) { + text = "Revert"; + } + else { + text = "Accept"; + } + + String actionId = side.select("Diff.ApplyLeftSide", "Diff.ApplyRightSide"); + Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(actionId); + String shortcutsText = StringUtil.nullize(KeymapUtil.getShortcutsText(shortcuts)); + String tooltipText = DiffUtil.createTooltipText(text, shortcutsText); + + return createIconRenderer(side, tooltipText, icon, () -> myViewer.replaceChange(myChange, side)); + } + + @Nullable + private GutterIconRenderer createAppendRenderer(@NotNull final Side side) { + return createIconRenderer(side, "Append", DiffUtil.getArrowDownIcon(side), () -> myViewer.appendChange(myChange, side)); + } + + @Nullable + private GutterIconRenderer createIconRenderer(@NotNull final Side sourceSide, + @NotNull final String tooltipText, + @NotNull final Icon icon, + @NotNull final Runnable perform) { + if (!DiffUtil.isEditable(myViewer.getEditor(sourceSide.other()))) return null; + return new DiffGutterRenderer(icon, tooltipText) { + @Override + protected void handleMouseClick() { + if (!myChange.isValid()) return; + final Project project = myViewer.getProject(); + final Document document = myViewer.getEditor(sourceSide.other()).getDocument(); + DiffUtil.executeWriteCommand(document, project, "Replace change", perform); + } + }; + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffModel.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffModel.java new file mode 100644 index 000000000000..ab13cc9a1507 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffModel.java @@ -0,0 +1,102 @@ +// Copyright 2000-2019 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.tools.simple; + +import com.intellij.diff.util.DiffUtil; +import com.intellij.diff.util.LineRange; +import com.intellij.diff.util.Side; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.util.ThreeState; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class SimpleDiffModel { + @NotNull private final SimpleDiffViewer myViewer; + + @NotNull private final List myValidChanges = new ArrayList<>(); + @NotNull private final List myAllChanges = new ArrayList<>(); + @NotNull private ThreeState myIsContentsEqual = ThreeState.UNSURE; + + @NotNull private final List myPresentations = new ArrayList<>(); + + public SimpleDiffModel(@NotNull SimpleDiffViewer viewer) { + myViewer = viewer; + } + + @NotNull + public ThreeState isContentsEqual() { + return myIsContentsEqual; + } + + @NotNull + public List getChanges() { + return myValidChanges; + } + + @NotNull + public List getAllChanges() { + return ContainerUtil.filter(myAllChanges, it -> !it.isDestroyed()); + } + + public void setChanges(@NotNull List changes, boolean isContentsEqual) { + clear(); + + for (int i = 0; i < changes.size(); i++) { + SimpleDiffChange change = changes.get(i); + SimpleDiffChange previousChange = i != 0 ? changes.get(i - 1) : null; + + SimpleDiffChangeUi changeUi = myViewer.createUi(change); + changeUi.installHighlighter(previousChange); + myPresentations.add(changeUi); + } + + myValidChanges.addAll(changes); + myAllChanges.addAll(changes); + myIsContentsEqual = ThreeState.fromBoolean(isContentsEqual); + } + + public void clear() { + for (SimpleDiffChangeUi changeUi : myPresentations) { + changeUi.destroyHighlighter(); + } + myValidChanges.clear(); + myAllChanges.clear(); + myPresentations.clear(); + myIsContentsEqual = ThreeState.UNSURE; + } + + public void destroyChange(@NotNull SimpleDiffChange change) { + SimpleDiffChangeUi changeUi = myPresentations.set(change.getIndex(), null); + if (changeUi != null) changeUi.destroyHighlighter(); + + myValidChanges.remove(change); + change.markDestroyed(); + } + + public void updateGutterActions(boolean force) { + for (SimpleDiffChangeUi changeUi : myPresentations) { + changeUi.updateGutterActions(force); + } + } + + public void handleBeforeDocumentChange(@NotNull Side side, @NotNull DocumentEvent e) { + if (myValidChanges.isEmpty()) return; + + LineRange lineRange = DiffUtil.getAffectedLineRange(e); + int shift = DiffUtil.countLinesShift(e); + + List invalidated = new ArrayList<>(); + for (SimpleDiffChange change : myValidChanges) { + if (change.processDocumentChange(lineRange.start, lineRange.end, shift, side)) { + invalidated.add(change); + + SimpleDiffChangeUi changeUi = myPresentations.get(change.getIndex()); + if (changeUi != null) changeUi.invalidate(); + } + } + + myValidChanges.removeAll(invalidated); + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index 40aa76fa3074..9dbb5eb8c6cc 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -42,6 +42,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.*; @@ -59,9 +60,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @NotNull private final PrevNextDifferenceIterable myPrevNextDifferenceIterable; @NotNull private final StatusPanel myStatusPanel; - @NotNull private final List myDiffChanges = new ArrayList<>(); - @NotNull private final List myInvalidDiffChanges = new ArrayList<>(); - private boolean myIsContentsEqual; + @NotNull private final SimpleDiffModel myModel = new SimpleDiffModel(this); @NotNull private final MyFoldingModel myFoldingModel; @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper(); @@ -97,7 +96,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @Override @CalledInAwt protected void onDispose() { - destroyChangedBlocks(); + myModel.clear(); myFoldingModel.destroy(); super.onDispose(); } @@ -161,6 +160,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { myInitialScrollHelper.updateContext(myRequest); } + @NotNull + protected SimpleDiffChangeUi createUi(@NotNull SimpleDiffChange change) { + return new SimpleDiffChangeUi(this, change); + } + // // Diff // @@ -197,7 +201,10 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { return apply(null, isContentsEqual); } else { - List changes = ContainerUtil.map(lineFragments, fragment -> new SimpleDiffChange(this, fragment)); + List changes = new ArrayList<>(); + for (LineFragment fragment : lineFragments) { + changes.add(new SimpleDiffChange(changes.size(), fragment)); + } return apply(changes, isContentsEqual); } } @@ -224,22 +231,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { clearDiffPresentation(); - myIsContentsEqual = isContentsEqual; if (isContentsEqual) { boolean equalCharsets = TextDiffViewerUtil.areEqualCharsets(getContents()); boolean equalSeparators = TextDiffViewerUtil.areEqualLineSeparators(getContents()); myPanel.addNotification(DiffNotifications.createEqualContents(equalCharsets, equalSeparators)); } - if (changes != null) { - for (int i = 0; i < changes.size(); i++) { - SimpleDiffChange change = changes.get(i); - LineFragment previousChangeFragment = i != 0 ? changes.get(i - 1).getFragment() : null; - change.installHighlighter(previousChangeFragment); - } - - myDiffChanges.addAll(changes); - } + myModel.setChanges(ContainerUtil.notNullize(changes), isContentsEqual); myFoldingModel.install(foldingState, myRequest, getFoldingModelSettings()); @@ -260,37 +258,23 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { } private void clearDiffPresentation() { - myStatusPanel.setBusy(false); + myModel.clear(); + myPanel.resetNotifications(); - destroyChangedBlocks(); + myStatusPanel.setBusy(false); + + myContentPanel.repaintDivider(); + myStatusPanel.update(); } // // Impl // - private void destroyChangedBlocks() { - myIsContentsEqual = false; - - for (SimpleDiffChange change : myDiffChanges) { - change.destroyHighlighter(); - } - myDiffChanges.clear(); - - for (SimpleDiffChange change : myInvalidDiffChanges) { - change.destroyHighlighter(); - } - myInvalidDiffChanges.clear(); - - myContentPanel.repaintDivider(); - myStatusPanel.update(); - } - @Override @CalledInAwt protected void onBeforeDocumentChange(@NotNull DocumentEvent e) { super.onBeforeDocumentChange(e); - if (myDiffChanges.isEmpty()) return; List documents = ContainerUtil.map(getEditors(), Editor::getDocument); Side side = Side.fromValue(documents, e.getDocument()); @@ -299,26 +283,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { return; } - LineRange lineRange = DiffUtil.getAffectedLineRange(e); - int shift = DiffUtil.countLinesShift(e); - - List invalid = new ArrayList<>(); - for (SimpleDiffChange change : myDiffChanges) { - if (change.processChange(lineRange.start, lineRange.end, shift, side)) { - invalid.add(change); - } - } - - if (!invalid.isEmpty()) { - myDiffChanges.removeAll(invalid); - myInvalidDiffChanges.addAll(invalid); - } + myModel.handleBeforeDocumentChange(side, e); } @CalledInAwt protected boolean doScrollToChange(@NotNull ScrollToPolicy scrollToPolicy) { SimpleDiffChange targetChange = scrollToPolicy.select(getNonSkippedDiffChanges()); - if (targetChange == null) targetChange = scrollToPolicy.select(myDiffChanges); + if (targetChange == null) targetChange = scrollToPolicy.select(getDiffChanges()); if (targetChange == null) return false; doScrollToChange(targetChange, false); @@ -358,12 +329,12 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @NotNull protected List getDiffChanges() { - return myDiffChanges; + return myModel.getChanges(); } @NotNull private List getNonSkippedDiffChanges() { - return ContainerUtil.filter(myDiffChanges, it -> !it.isSkipped()); + return ContainerUtil.filter(myModel.getChanges(), it -> !it.isSkipped()); } @NotNull @@ -409,10 +380,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { } protected boolean isSomeChangeSelected(@NotNull Side side) { - if (myDiffChanges.isEmpty()) return false; + if (getDiffChanges().isEmpty()) return false; EditorEx editor = getEditor(side); - return DiffUtil.isSomeRangeSelected(editor, lines -> ContainerUtil.exists(myDiffChanges, change -> isChangeSelected(change, lines, side))); + return DiffUtil.isSomeRangeSelected(editor, lines -> + ContainerUtil.exists(getDiffChanges(), change -> isChangeSelected(change, lines, side))); } @NotNull @@ -420,7 +392,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { protected List getSelectedChanges(@NotNull Side side) { EditorEx editor = getEditor(side); BitSet lines = DiffUtil.getSelectedLines(editor); - return ContainerUtil.filter(myDiffChanges, change -> isChangeSelected(change, lines, side)); + return ContainerUtil.filter(getDiffChanges(), change -> isChangeSelected(change, lines, side)); } private static boolean isChangeSelected(SimpleDiffChange change, @NotNull BitSet lines, @NotNull Side side) { @@ -434,7 +406,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { protected SimpleDiffChange getSelectedChange(@NotNull Side side) { int caretLine = getEditor(side).getCaretModel().getLogicalPosition().line; - for (SimpleDiffChange change : myDiffChanges) { + for (SimpleDiffChange change : getDiffChanges()) { int line1 = change.getStartLine(side); int line2 = change.getEndLine(side); @@ -484,9 +456,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @Override protected void doApply(boolean readOnly) { super.doApply(readOnly); - for (SimpleDiffChange change : myDiffChanges) { - change.updateGutterActions(true); - } + myModel.updateGutterActions(true); } } @@ -631,8 +601,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { DiffUtil.applyModification(getEditor(outputSide).getDocument(), change.getStartLine(outputSide), change.getEndLine(outputSide), getEditor(sourceSide).getDocument(), change.getStartLine(sourceSide), change.getEndLine(sourceSide)); - change.destroyHighlighter(); - myDiffChanges.remove(change); + myModel.destroyChange(change); } @CalledWithWriteLock @@ -644,8 +613,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { DiffUtil.applyModification(getEditor(outputSide).getDocument(), change.getEndLine(outputSide), change.getEndLine(outputSide), getEditor(sourceSide).getDocument(), change.getStartLine(sourceSide), change.getEndLine(sourceSide)); - change.destroyHighlighter(); - myDiffChanges.remove(change); + myModel.destroyChange(change); } private class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction { @@ -672,12 +640,12 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @Override public boolean hasNextBlock() { - return myIndex < myDiffChanges.size(); + return myIndex < getDiffChanges().size(); } @Override public void loadNextBlock() { - SimpleDiffChange change = myDiffChanges.get(myIndex); + SimpleDiffChange change = getDiffChanges().get(myIndex); myIndex++; int line1 = change.getStartLine(Side.RIGHT); @@ -723,14 +691,14 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @NotNull @Override public Range getRange(@NotNull Side baseSide, int line) { - if (myDiffChanges.isEmpty()) return idRange(line); + if (getDiffChanges().isEmpty()) return idRange(line); return super.getRange(baseSide, line); } @Override protected void processHelper(@NotNull ScrollHelper helper) { if (!helper.process(0, 0)) return; - for (SimpleDiffChange diffChange : myDiffChanges) { + for (SimpleDiffChange diffChange : getDiffChanges()) { if (!helper.process(diffChange.getStartLine(Side.LEFT), diffChange.getStartLine(Side.RIGHT))) return; if (!helper.process(diffChange.getEndLine(Side.LEFT), diffChange.getEndLine(Side.RIGHT))) return; } @@ -755,7 +723,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @Override public void process(@NotNull Handler handler) { - for (SimpleDiffChange diffChange : myDiffChanges) { + for (SimpleDiffChange diffChange : getDiffChanges()) { if (!handler.processExcludable(diffChange.getStartLine(Side.LEFT), diffChange.getEndLine(Side.LEFT), diffChange.getStartLine(Side.RIGHT), diffChange.getEndLine(Side.RIGHT), getEditor1(), diffChange.getDiffType(), diffChange.isExcluded())) { @@ -772,10 +740,10 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { if (myTextDiffProvider.isHighlightingDisabled()) { return DiffBundle.message("diff.highlighting.disabled.text"); } - int excludedChanges = ContainerUtil.count(myDiffChanges, it -> it.isExcluded()) + - ContainerUtil.count(myInvalidDiffChanges, it -> it.isExcluded()); - int changesCount = myDiffChanges.size() + myInvalidDiffChanges.size(); - if (changesCount == 0 && !myIsContentsEqual) { + List allChanges = myModel.getAllChanges(); + int excludedChanges = ContainerUtil.count(allChanges, it -> it.isExcluded()); + int changesCount = allChanges.size(); + if (changesCount == 0 && myModel.isContentsEqual() == ThreeState.NO) { return DiffBundle.message("diff.all.differences.ignored.text"); } String message = DiffBundle.message("diff.count.differences.status.text", changesCount - excludedChanges); @@ -791,9 +759,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @Override public void onModifiersChanged() { - for (SimpleDiffChange change : myDiffChanges) { - change.updateGutterActions(false); - } + myModel.updateGutterActions(false); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java index f11941307ea7..0b69095807e5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java @@ -5,6 +5,7 @@ import com.intellij.diff.DiffContext; import com.intellij.diff.comparison.DiffTooBigException; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.tools.simple.SimpleDiffChange; +import com.intellij.diff.tools.simple.SimpleDiffChangeUi; import com.intellij.diff.tools.simple.SimpleDiffViewer; import com.intellij.diff.tools.util.DiffNotifications; import com.intellij.diff.util.DiffGutterRenderer; @@ -114,6 +115,13 @@ public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { return group; } + @NotNull + @Override + protected SimpleDiffChangeUi createUi(@NotNull SimpleDiffChange change) { + if (change instanceof MySimpleDiffChange) return new MySimpleDiffChangeUi(this, (MySimpleDiffChange)change); + return super.createUi(change); + } + @Override @NotNull protected Runnable performRediff(@NotNull final ProgressIndicator indicator) { @@ -218,7 +226,10 @@ public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { boolean isSkipped = !isFromActiveChangelist; boolean isExcluded = !isFromActiveChangelist || (myAllowExcludeChangesFromCommit && isExcludedFromCommit); - changes.addAll(ContainerUtil.map(rangeFragments, fragment -> new MySimpleDiffChange(fragment, isExcluded, isSkipped, localRange.getChangelistId(), isExcludedFromCommit))); + for (LineFragment fragment : rangeFragments) { + changes.add(new MySimpleDiffChange(changes.size(), fragment, isExcluded, isSkipped, + localRange.getChangelistId(), isFromActiveChangelist, isExcludedFromCommit)); + } } return apply(changes, isContentsEqual); @@ -230,37 +241,57 @@ public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { myExcludeAllCheckboxPanel.refresh(); } - private class MySimpleDiffChange extends SimpleDiffChange { + private static class MySimpleDiffChange extends SimpleDiffChange { @NotNull private final String myChangelistId; + private final boolean myIsFromActiveChangelist; private final boolean myIsExcludedFromCommit; - MySimpleDiffChange(@NotNull LineFragment fragment, - boolean isExcluded, - boolean isSkipped, - @NotNull String changelistId, - boolean isExcludedFromCommit) { - super(SimpleLocalChangeListDiffViewer.this, fragment, isExcluded, isSkipped); + MySimpleDiffChange(int index, + @NotNull LineFragment fragment, + boolean isExcluded, + boolean isSkipped, + @NotNull String changelistId, + boolean isFromActiveChangelist, + boolean isExcludedFromCommit) { + super(index, fragment, isExcluded, isSkipped); myChangelistId = changelistId; + myIsFromActiveChangelist = isFromActiveChangelist; myIsExcludedFromCommit = isExcludedFromCommit; } + @NotNull + public String getChangelistId() { + return myChangelistId; + } + + public boolean isFromActiveChangelist() { + return myIsFromActiveChangelist; + } + + public boolean isExcludedFromCommit() { + return myIsExcludedFromCommit; + } + } + + private static class MySimpleDiffChangeUi extends SimpleDiffChangeUi { + private MySimpleDiffChangeUi(@NotNull SimpleLocalChangeListDiffViewer viewer, @NotNull MySimpleDiffChange change) { + super(viewer, change); + } + @NotNull private SimpleLocalChangeListDiffViewer getViewer() { return (SimpleLocalChangeListDiffViewer)myViewer; } - public boolean isFromActiveChangelist() { - return myChangelistId.equals(getViewer().myChangelistId); - } - - public boolean isExcludedFromCommit() { - return myIsExcludedFromCommit; + @NotNull + private MySimpleDiffChange getChange() { + return ((MySimpleDiffChange)myChange); } @Override protected void doInstallActionHighlighters() { super.doInstallActionHighlighters(); - if (myAllowExcludeChangesFromCommit && isFromActiveChangelist()) { + if (getViewer().myAllowExcludeChangesFromCommit && getChange().isFromActiveChangelist()) { myOperations.add(new ExcludeGutterOperation()); } } @@ -272,20 +303,21 @@ public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { @Override public GutterIconRenderer createRenderer() { - if (!isFromActiveChangelist()) return null; + if (!getChange().isFromActiveChangelist()) return null; - Icon icon = myIsExcludedFromCommit ? AllIcons.Diff.GutterCheckBox : AllIcons.Diff.GutterCheckBoxSelected; + final boolean isExcludedFromCommit = getChange().isExcludedFromCommit(); + Icon icon = isExcludedFromCommit ? AllIcons.Diff.GutterCheckBox : AllIcons.Diff.GutterCheckBoxSelected; return new DiffGutterRenderer(icon, "Include into commit") { @Override protected void handleMouseClick() { - if (!isValid()) return; + if (!myChange.isValid()) return; PartialLocalLineStatusTracker tracker = getViewer().getPartialTracker(); if (tracker == null) return; - LocalRange range = tracker.getRangeForLine(getStartLine(Side.RIGHT)); + LocalRange range = tracker.getRangeForLine(myChange.getStartLine(Side.RIGHT)); if (range == null) return; - tracker.setExcludedFromCommit(range, !myIsExcludedFromCommit); + tracker.setExcludedFromCommit(range, !isExcludedFromCommit); getViewer().rediff(); }