diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/ChangedBlock.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/ChangedBlock.java deleted file mode 100644 index f20be69d1bfe..000000000000 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/ChangedBlock.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.tools.fragmented; - -import com.intellij.diff.fragments.LineFragment; -import com.intellij.diff.util.LineRange; -import org.jetbrains.annotations.NotNull; - -class ChangedBlock { - @NotNull private final LineRange myRange1; - @NotNull private final LineRange myRange2; - - @NotNull private final LineFragment myLineFragment; - - ChangedBlock(@NotNull LineRange range1, - @NotNull LineRange range2, - @NotNull LineFragment lineFragment) { - myRange1 = range1; - myRange2 = range2; - myLineFragment = lineFragment; - } - - @NotNull - public LineRange getRange1() { - return myRange1; - } - - @NotNull - public LineRange getRange2() { - return myRange2; - } - - public int getLine1() { - return myRange1.start; - } - - public int getLine2() { - return myRange2.end; - } - - @NotNull - public LineFragment getLineFragment() { - return myLineFragment; - } -} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChange.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChange.java index c4aa1d5ceead..f0b38e8c21ce 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChange.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChange.java @@ -16,84 +16,29 @@ package com.intellij.diff.tools.fragmented; import com.intellij.diff.fragments.LineFragment; -import com.intellij.diff.util.*; +import com.intellij.diff.util.DiffUtil; import com.intellij.diff.util.DiffUtil.UpdatedLineRange; -import com.intellij.icons.AllIcons; -import com.intellij.openapi.editor.Document; -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.project.Project; +import com.intellij.diff.util.LineRange; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.List; public class UnifiedDiffChange { - @NotNull private final UnifiedDiffViewer myViewer; - @NotNull private final EditorEx myEditor; - // Boundaries of this change in myEditor. If current state is out-of-date - approximate value. private int myLine1; private int myLine2; + @NotNull private final LineRange myDeletedRange; + @NotNull private final LineRange myInsertedRange; @NotNull private final LineFragment myLineFragment; - @NotNull private final List myHighlighters = new ArrayList<>(); - @NotNull private final List myOperations = new ArrayList<>(); - - public UnifiedDiffChange(@NotNull UnifiedDiffViewer viewer, @NotNull ChangedBlock block) { - myViewer = viewer; - myEditor = viewer.getEditor(); - - myLine1 = block.getLine1(); - myLine2 = block.getLine2(); - myLineFragment = block.getLineFragment(); - - LineRange deleted = block.getRange1(); - LineRange inserted = block.getRange2(); - - installHighlighter(deleted, inserted); - } - - public void destroyHighlighter() { - for (RangeHighlighter highlighter : myHighlighters) { - highlighter.dispose(); - } - myHighlighters.clear(); - - for (MyGutterOperation operation : myOperations) { - operation.dispose(); - } - myOperations.clear(); - } - - private void installHighlighter(@NotNull LineRange deleted, @NotNull LineRange inserted) { - assert myHighlighters.isEmpty(); - - doInstallHighlighters(deleted, inserted); - doInstallActionHighlighters(); - } - - private void doInstallActionHighlighters() { - boolean leftEditable = myViewer.isEditable(Side.LEFT, false); - boolean rightEditable = myViewer.isEditable(Side.RIGHT, false); - - if (leftEditable && rightEditable) { - myOperations.add(createOperation(Side.LEFT)); - myOperations.add(createOperation(Side.RIGHT)); - } - else if (rightEditable) { - myOperations.add(createOperation(Side.LEFT)); - } - } - - private void doInstallHighlighters(@NotNull LineRange deleted, @NotNull LineRange inserted) { - myHighlighters.addAll(DiffDrawUtil.createUnifiedChunkHighlighters(myEditor, deleted, inserted, myLineFragment.getInnerFragments())); + public UnifiedDiffChange(int blockStart, + int insertedStart, + int blockEnd, + @NotNull LineFragment lineFragment) { + myLine1 = blockStart; + myLine2 = blockEnd; + myDeletedRange = new LineRange(blockStart, insertedStart); + myInsertedRange = new LineRange(insertedStart, blockEnd); + myLineFragment = lineFragment; } public int getLine1() { @@ -112,86 +57,19 @@ public class UnifiedDiffChange { return myLineFragment; } + @NotNull + public LineRange getDeletedRange() { + return myDeletedRange; + } + + @NotNull + public LineRange getInsertedRange() { + return myInsertedRange; + } + public void processChange(int oldLine1, int oldLine2, int shift) { UpdatedLineRange newRange = DiffUtil.updateRangeOnModification(myLine1, myLine2, oldLine1, oldLine2, shift); myLine1 = newRange.startLine; myLine2 = newRange.endLine; } - - // - // Gutter - // - - public void updateGutterActions() { - for (MyGutterOperation operation : myOperations) { - operation.update(); - } - } - - @NotNull - private MyGutterOperation createOperation(@NotNull Side sourceSide) { - int offset = myEditor.getDocument().getLineStartOffset(myLine1); - RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(offset, offset, - HighlighterLayer.ADDITIONAL_SYNTAX, - null, - HighlighterTargetArea.LINES_IN_RANGE); - return new MyGutterOperation(sourceSide, highlighter); - } - - private class MyGutterOperation { - @NotNull private final Side mySide; - @NotNull private final RangeHighlighter myHighlighter; - - private MyGutterOperation(@NotNull Side sourceSide, @NotNull RangeHighlighter highlighter) { - mySide = sourceSide; - myHighlighter = highlighter; - - update(); - } - - public void dispose() { - myHighlighter.dispose(); - } - - public void update() { - if (myHighlighter.isValid()) myHighlighter.setGutterIconRenderer(createRenderer()); - } - - @Nullable - public GutterIconRenderer createRenderer() { - if (myViewer.isStateIsOutOfDate()) return null; - if (!myViewer.isEditable(mySide.other(), true)) return null; - - if (mySide.isLeft()) { - return createIconRenderer(mySide, "Revert", AllIcons.Diff.Remove); - } - else { - return createIconRenderer(mySide, "Accept", AllIcons.Actions.Checked); - } - } - } - - @Nullable - private GutterIconRenderer createIconRenderer(@NotNull final Side sourceSide, - @NotNull final String tooltipText, - @NotNull final Icon icon) { - return new DiffGutterRenderer(icon, tooltipText) { - @Override - protected void handleMouseClick() { - if (myViewer.isStateIsOutOfDate()) return; - if (!myViewer.isEditable(sourceSide.other(), true)) return; - - final Project project = myViewer.getProject(); - final Document document = myViewer.getDocument(sourceSide.other()); - - DiffUtil.executeWriteCommand(document, project, "Replace change", () -> { - myViewer.replaceChange(UnifiedDiffChange.this, sourceSide); - myViewer.scheduleRediff(); - }); - // applyChange() will schedule rediff, but we want to try to do it in sync - // and we can't do it inside write action - myViewer.rediff(); - } - }; - } } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChangeUi.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChangeUi.java new file mode 100644 index 000000000000..ef9ca6ad9c1a --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChangeUi.java @@ -0,0 +1,151 @@ +// 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.fragmented; + +import com.intellij.diff.util.DiffDrawUtil; +import com.intellij.diff.util.DiffGutterRenderer; +import com.intellij.diff.util.DiffUtil; +import com.intellij.diff.util.Side; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.editor.Document; +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.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class UnifiedDiffChangeUi { + @NotNull private final UnifiedDiffViewer myViewer; + @NotNull private final EditorEx myEditor; + @NotNull private final UnifiedDiffChange myChange; + + @NotNull private final List myHighlighters = new ArrayList<>(); + @NotNull private final List myOperations = new ArrayList<>(); + + public UnifiedDiffChangeUi(@NotNull UnifiedDiffViewer viewer, @NotNull UnifiedDiffChange change) { + myViewer = viewer; + myEditor = viewer.getEditor(); + myChange = change; + } + + public void destroyHighlighter() { + for (RangeHighlighter highlighter : myHighlighters) { + highlighter.dispose(); + } + myHighlighters.clear(); + + for (MyGutterOperation operation : myOperations) { + operation.dispose(); + } + myOperations.clear(); + } + + public void installHighlighter() { + assert myHighlighters.isEmpty() && myOperations.isEmpty(); + + doInstallHighlighters(); + doInstallActionHighlighters(); + } + + private void doInstallActionHighlighters() { + boolean leftEditable = myViewer.isEditable(Side.LEFT, false); + boolean rightEditable = myViewer.isEditable(Side.RIGHT, false); + + if (leftEditable && rightEditable) { + myOperations.add(createOperation(Side.LEFT)); + myOperations.add(createOperation(Side.RIGHT)); + } + else if (rightEditable) { + myOperations.add(createOperation(Side.LEFT)); + } + } + + private void doInstallHighlighters() { + myHighlighters.addAll(DiffDrawUtil.createUnifiedChunkHighlighters(myEditor, + myChange.getDeletedRange(), + myChange.getInsertedRange(), + myChange.getLineFragment().getInnerFragments())); + } + + // + // Gutter + // + + public void updateGutterActions() { + for (MyGutterOperation operation : myOperations) { + operation.update(); + } + } + + @NotNull + private MyGutterOperation createOperation(@NotNull Side sourceSide) { + int offset = myEditor.getDocument().getLineStartOffset(myChange.getLine1()); + RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(offset, offset, + HighlighterLayer.ADDITIONAL_SYNTAX, + null, + HighlighterTargetArea.LINES_IN_RANGE); + return new MyGutterOperation(sourceSide, highlighter); + } + + private class MyGutterOperation { + @NotNull private final Side mySide; + @NotNull private final RangeHighlighter myHighlighter; + + private MyGutterOperation(@NotNull Side sourceSide, @NotNull RangeHighlighter highlighter) { + mySide = sourceSide; + myHighlighter = highlighter; + + update(); + } + + public void dispose() { + myHighlighter.dispose(); + } + + public void update() { + if (myHighlighter.isValid()) myHighlighter.setGutterIconRenderer(createRenderer()); + } + + @Nullable + public GutterIconRenderer createRenderer() { + if (myViewer.isStateIsOutOfDate()) return null; + if (!myViewer.isEditable(mySide.other(), true)) return null; + + if (mySide.isLeft()) { + return createIconRenderer(mySide, "Revert", AllIcons.Diff.Remove); + } + else { + return createIconRenderer(mySide, "Accept", AllIcons.Actions.Checked); + } + } + } + + private GutterIconRenderer createIconRenderer(@NotNull final Side sourceSide, + @NotNull final String tooltipText, + @NotNull final Icon icon) { + return new DiffGutterRenderer(icon, tooltipText) { + @Override + protected void handleMouseClick() { + if (myViewer.isStateIsOutOfDate()) return; + if (!myViewer.isEditable(sourceSide.other(), true)) return; + + final Project project = myViewer.getProject(); + final Document document = myViewer.getDocument(sourceSide.other()); + + DiffUtil.executeWriteCommand(document, project, "Replace change", () -> { + myViewer.replaceChange(myChange, sourceSide); + myViewer.scheduleRediff(); + }); + // applyChange() will schedule rediff, but we want to try to do it in sync + // and we can't do it inside write action + myViewer.rediff(); + } + }; + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffModel.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffModel.java new file mode 100644 index 000000000000..04c2659b9084 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffModel.java @@ -0,0 +1,115 @@ +// 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.fragmented; + +import com.intellij.diff.util.Side; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.util.ThreeState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class UnifiedDiffModel { + @NotNull private final UnifiedDiffViewer myViewer; + + @Nullable private ChangedBlockData myData = null; + @NotNull private ThreeState myIsContentsEqual = ThreeState.UNSURE; + + @NotNull private final List myPresentations = new ArrayList<>(); + @NotNull private final List myGuardedRangeBlocks = new ArrayList<>(); + + public UnifiedDiffModel(@NotNull UnifiedDiffViewer viewer) { + myViewer = viewer; + } + + public boolean isValid() { + return myData != null; + } + + @NotNull + public ThreeState isContentsEqual() { + return myIsContentsEqual; + } + + @Nullable + public ChangedBlockData getData() { + return myData; + } + + @Nullable + public List getDiffChanges() { + return myData != null ? myData.getDiffChanges() : null; + } + + @Nullable + public LineNumberConvertor getLineNumberConvertor(@NotNull Side side) { + return myData != null ? myData.getLineNumberConvertor(side) : null; + } + + public void setChanges(@NotNull List changes, + boolean isContentsEqual, + @NotNull List guardedBlocks, + @NotNull LineNumberConvertor convertor1, + @NotNull LineNumberConvertor convertor2) { + assert myPresentations.isEmpty() && myGuardedRangeBlocks.isEmpty() && myData == null; + + for (UnifiedDiffChange change : changes) { + UnifiedDiffChangeUi changeUi = myViewer.createUi(change); + changeUi.installHighlighter(); + myPresentations.add(changeUi); + } + + myGuardedRangeBlocks.addAll(guardedBlocks); + + myData = new ChangedBlockData(changes, convertor1, convertor2); + myIsContentsEqual = ThreeState.fromBoolean(isContentsEqual); + } + + public void clear() { + for (UnifiedDiffChangeUi changeUi : myPresentations) { + changeUi.destroyHighlighter(); + } + myPresentations.clear(); + + DocumentEx document = myViewer.getEditor().getDocument(); + for (RangeMarker block : myGuardedRangeBlocks) { + document.removeGuardedBlock(block); + } + myGuardedRangeBlocks.clear(); + + myData = null; + myIsContentsEqual = ThreeState.UNSURE; + } + + public void updateGutterActions() { + for (UnifiedDiffChangeUi changeUi : myPresentations) { + changeUi.updateGutterActions(); + } + } + + public static class ChangedBlockData { + @NotNull private final List myDiffChanges; + @NotNull private final LineNumberConvertor myLineNumberConvertor1; + @NotNull private final LineNumberConvertor myLineNumberConvertor2; + + ChangedBlockData(@NotNull List diffChanges, + @NotNull LineNumberConvertor lineNumberConvertor1, + @NotNull LineNumberConvertor lineNumberConvertor2) { + myDiffChanges = diffChanges; + myLineNumberConvertor1 = lineNumberConvertor1; + myLineNumberConvertor2 = lineNumberConvertor2; + } + + @NotNull + public List getDiffChanges() { + return myDiffChanges; + } + + @NotNull + public LineNumberConvertor getLineNumberConvertor(@NotNull Side side) { + return side.select(myLineNumberConvertor1, myLineNumberConvertor2); + } + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java index d5fe4410a294..c2aa809c026e 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java @@ -11,6 +11,7 @@ import com.intellij.diff.contents.DocumentContent; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.requests.DiffRequest; +import com.intellij.diff.tools.fragmented.UnifiedDiffModel.ChangedBlockData; import com.intellij.diff.tools.util.*; import com.intellij.diff.tools.util.base.InitialScrollPositionSupport; import com.intellij.diff.tools.util.base.ListenerDiffViewerBase; @@ -44,6 +45,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.Navigatable; +import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import gnu.trove.TIntFunction; import org.jetbrains.annotations.*; @@ -69,7 +71,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @NotNull protected Side myMasterSide = Side.RIGHT; - @Nullable private ChangedBlockData myChangedBlockData; + @NotNull private final UnifiedDiffModel myModel = new UnifiedDiffModel(this); private final boolean[] myForceReadOnlyFlags; private boolean myReadOnlyLockSet = false; @@ -130,6 +132,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @Override @CalledInAwt protected void onDispose() { + myModel.clear(); myFoldingModel.destroy(); super.onDispose(); EditorFactory.getInstance().releaseEditor(myEditor); @@ -217,6 +220,11 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { new TextDiffViewerUtil.EditorActionsPopup(createEditorPopupActions()).install(getEditors()); } + @NotNull + protected UnifiedDiffChangeUi createUi(@NotNull UnifiedDiffChange change) { + return new UnifiedDiffChangeUi(this, change); + } + // // Diff // @@ -276,7 +284,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { getDocument(masterSide), masterSide.select(convertor1, convertor2), StringUtil.countNewLines(builder.getText()) + 1); - return apply(editorData, builder.getBlocks(), convertor1, convertor2, foldingState, isContentsEqual); + return apply(editorData, builder.getChanges(), convertor1, convertor2, foldingState, isContentsEqual); } catch (DiffTooBigException e) { return () -> { @@ -316,11 +324,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { protected void markStateIsOutOfDate() { myStateIsOutOfDate = true; myFoldingModel.disposeLineConvertor(); - if (myChangedBlockData != null) { - for (UnifiedDiffChange diffChange : myChangedBlockData.getDiffChanges()) { - diffChange.updateGutterActions(); - } - } + myModel.updateGutterActions(); } @Nullable @@ -343,7 +347,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @NotNull private Runnable apply(@NotNull final CombinedEditorData data, - @NotNull final List blocks, + @NotNull final List diffChanges, @NotNull final LineNumberConvertor convertor1, @NotNull final LineNumberConvertor convertor2, @Nullable final FoldingModelSupport.Data foldingState, @@ -386,16 +390,10 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { if (data.getRangeHighlighter() != null) data.getRangeHighlighter().apply(myProject, myDocument); - - ArrayList diffChanges = new ArrayList<>(blocks.size()); - for (ChangedBlock block : blocks) { - diffChanges.add(new UnifiedDiffChange(this, block)); - } - List guarderRangeBlocks = new ArrayList<>(); if (!myEditor.isViewer()) { - for (ChangedBlock block : blocks) { - LineRange range = myMasterSide.select(block.getRange2(), block.getRange1()); + for (UnifiedDiffChange change : diffChanges) { + LineRange range = myMasterSide.select(change.getInsertedRange(), change.getDeletedRange()); if (range.isEmpty()) continue; TextRange textRange = DiffUtil.getLinesRange(myDocument, range.start, range.end); guarderRangeBlocks.add(createGuardedBlock(textRange.getStartOffset(), textRange.getEndOffset())); @@ -404,9 +402,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { guarderRangeBlocks.add(createGuardedBlock(textLength, textLength)); } - - myChangedBlockData = new ChangedBlockData(diffChanges, guarderRangeBlocks, convertor1, convertor2, isContentsEqual); - + myModel.setChanges(diffChanges, isContentsEqual, guarderRangeBlocks, convertor1, convertor2); int newCaretLine = transferLineToOneside(oldCaretLineTwoside.second, oldCaretLineTwoside.second.select(oldCaretLineTwoside.first)); @@ -442,8 +438,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { */ @CalledInAwt public int transferLineToOnesideStrict(@NotNull Side side, int line) { - if (myChangedBlockData == null) return -1; - return myChangedBlockData.getLineNumberConvertor(side).convertInv(line); + LineNumberConvertor convertor = myModel.getLineNumberConvertor(side); + return convertor != null ? convertor.convertInv(line) : -1; } /* @@ -451,8 +447,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { */ @CalledInAwt public int transferLineFromOnesideStrict(@NotNull Side side, int line) { - if (myChangedBlockData == null) return -1; - return myChangedBlockData.getLineNumberConvertor(side).convert(line); + LineNumberConvertor convertor = myModel.getLineNumberConvertor(side); + return convertor != null ? convertor.convert(line) : -1; } /* @@ -460,8 +456,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { */ @CalledInAwt public int transferLineToOneside(@NotNull Side side, int line) { - if (myChangedBlockData == null) return line; - return myChangedBlockData.getLineNumberConvertor(side).convertApproximateInv(line); + LineNumberConvertor convertor = myModel.getLineNumberConvertor(side); + return convertor != null ? convertor.convertApproximateInv(line) : line; } /* @@ -472,14 +468,15 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { public Pair transferLineFromOneside(int line) { int[] lines = new int[2]; - if (myChangedBlockData == null) { + ChangedBlockData blockData = myModel.getData(); + if (blockData == null) { lines[0] = line; lines[1] = line; return Pair.create(lines, myMasterSide); } - LineNumberConvertor lineConvertor1 = myChangedBlockData.getLineNumberConvertor(Side.LEFT); - LineNumberConvertor lineConvertor2 = myChangedBlockData.getLineNumberConvertor(Side.RIGHT); + LineNumberConvertor lineConvertor1 = blockData.getLineNumberConvertor(Side.LEFT); + LineNumberConvertor lineConvertor2 = blockData.getLineNumberConvertor(Side.RIGHT); Side side = myMasterSide; lines[0] = lineConvertor1.convert(line); @@ -503,15 +500,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @CalledInAwt private void destroyChangedBlockData() { - if (myChangedBlockData == null) return; - - for (UnifiedDiffChange change : myChangedBlockData.getDiffChanges()) { - change.destroyHighlighter(); - } - for (RangeMarker block : myChangedBlockData.getGuardedRangeBlocks()) { - myDocument.removeGuardedBlock(block); - } - myChangedBlockData = null; + myModel.clear(); UnifiedEditorRangeHighlighter.erase(myProject, myDocument); @@ -528,8 +517,9 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @Override public void beforeDocumentChange(@NotNull DocumentEvent e) { if (myDuringOnesideDocumentModification) return; - if (myChangedBlockData == null) { - LOG.warn("oneside beforeDocumentChange - myChangedBlockData == null"); + ChangedBlockData blockData = myModel.getData(); + if (blockData == null) { + LOG.warn("oneside beforeDocumentChange - model is invalid"); return; } // TODO: modify Document guard range logic - we can handle case, when whole read-only block is modified (ex: my replacing selection). @@ -559,12 +549,12 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { int twosideEndOffset = twosideDocument.getLineStartOffset(twosideEndLine) + onesideEndPosition.column; twosideDocument.replaceString(twosideStartOffset, twosideEndOffset, e.getNewFragment()); - for (UnifiedDiffChange change : myChangedBlockData.getDiffChanges()) { + for (UnifiedDiffChange change : blockData.getDiffChanges()) { change.processChange(line1, line2, shift); } - LineNumberConvertor masterConvertor = myChangedBlockData.getLineNumberConvertor(myMasterSide); - LineNumberConvertor slaveConvertor = myChangedBlockData.getLineNumberConvertor(myMasterSide.other()); + LineNumberConvertor masterConvertor = blockData.getLineNumberConvertor(myMasterSide); + LineNumberConvertor slaveConvertor = blockData.getLineNumberConvertor(myMasterSide.other()); masterConvertor.handleMasterChange(line1, line2, shift, true); slaveConvertor.handleMasterChange(line1, line2, shift, false); } @@ -670,9 +660,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { } protected boolean isSomeChangeSelected() { - if (myChangedBlockData == null) return false; - List changes = myChangedBlockData.getDiffChanges(); - if (changes.isEmpty()) return false; + List changes = myModel.getDiffChanges(); + if (changes == null || changes.isEmpty()) return false; return DiffUtil.isSomeRangeSelected(getEditor(), lines -> ContainerUtil.exists(changes, change -> isChangeSelected(change, lines))); } @@ -680,9 +669,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @NotNull @CalledInAwt private List getSelectedChanges() { - if (myChangedBlockData == null) return Collections.emptyList(); final BitSet lines = DiffUtil.getSelectedLines(myEditor); - List changes = myChangedBlockData.getDiffChanges(); + List changes = ContainerUtil.notNullize(myModel.getDiffChanges()); return ContainerUtil.filter(changes, change -> isChangeSelected(change, lines)); } @@ -821,7 +809,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @CalledInAwt @Nullable protected List getDiffChanges() { - return myChangedBlockData == null ? null : myChangedBlockData.getDiffChanges(); + return myModel.getDiffChanges(); } @NotNull @@ -872,10 +860,12 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @CalledInAwt @Nullable protected UnifiedDiffChange getCurrentChange() { - if (myChangedBlockData == null) return null; + List changes = myModel.getDiffChanges(); + if (changes == null) return null; + int caretLine = myEditor.getCaretModel().getLogicalPosition().line; - for (UnifiedDiffChange change : myChangedBlockData.getDiffChanges()) { + for (UnifiedDiffChange change : changes) { if (DiffUtil.isSelectedByLine(caretLine, change.getLine1(), change.getLine2())) return change; } return null; @@ -896,7 +886,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { } public boolean isContentGood() { - return myPanel.isGoodContent() && myChangedBlockData != null; + return myPanel.isGoodContent() && myModel.isValid(); } public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) { @@ -960,11 +950,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @Override protected void doApply(boolean readOnly) { myReadOnlyLockSet = readOnly; - if (myChangedBlockData != null) { - for (UnifiedDiffChange unifiedDiffChange : myChangedBlockData.getDiffChanges()) { - unifiedDiffChange.updateGutterActions(); - } - } + myModel.updateGutterActions(); updateEditorCanBeTyped(); putEditorHint(myEditor, readOnly && isEditable(myMasterSide, false)); } @@ -1043,9 +1029,11 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @Nullable @Override protected String getMessage() { - if (myChangedBlockData == null) return null; - int changesCount = myChangedBlockData.getDiffChanges().size(); - if (changesCount == 0 && !myChangedBlockData.isContentsEqual()) { + ChangedBlockData blockData = myModel.getData(); + + if (blockData == null) return null; + int changesCount = blockData.getDiffChanges().size(); + if (changesCount == 0 && myModel.isContentsEqual() == ThreeState.NO) { return DiffBundle.message("diff.all.differences.ignored.text"); } return DiffBundle.message("diff.count.differences.status.text", changesCount); @@ -1081,45 +1069,6 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { } } - private static class ChangedBlockData { - @NotNull private final List myDiffChanges; - @NotNull private final List myGuardedRangeBlocks; - @NotNull private final LineNumberConvertor myLineNumberConvertor1; - @NotNull private final LineNumberConvertor myLineNumberConvertor2; - private final boolean myIsContentsEqual; - - ChangedBlockData(@NotNull List diffChanges, - @NotNull List guarderRangeBlocks, - @NotNull LineNumberConvertor lineNumberConvertor1, - @NotNull LineNumberConvertor lineNumberConvertor2, - boolean isContentsEqual) { - myDiffChanges = diffChanges; - myGuardedRangeBlocks = guarderRangeBlocks; - myLineNumberConvertor1 = lineNumberConvertor1; - myLineNumberConvertor2 = lineNumberConvertor2; - myIsContentsEqual = isContentsEqual; - } - - @NotNull - public List getDiffChanges() { - return myDiffChanges; - } - - @NotNull - public List getGuardedRangeBlocks() { - return myGuardedRangeBlocks; - } - - @NotNull - public LineNumberConvertor getLineNumberConvertor(@NotNull Side side) { - return side.select(myLineNumberConvertor1, myLineNumberConvertor2); - } - - public boolean isContentsEqual() { - return myIsContentsEqual; - } - } - private static class CombinedEditorData { @NotNull private final CharSequence myText; @Nullable private final EditorHighlighter myHighlighter; @@ -1230,8 +1179,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { } private boolean doScrollToChange(@NotNull ScrollToPolicy scrollToChangePolicy) { - if (myChangedBlockData == null) return false; - List changes = myChangedBlockData.getDiffChanges(); + List changes = myModel.getDiffChanges(); + if (changes == null) return false; UnifiedDiffChange targetChange = scrollToChangePolicy.select(changes); if (targetChange == null) return false; @@ -1254,9 +1203,11 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @Override protected boolean doScrollToContext() { if (myNavigationContext == null) return false; - if (myChangedBlockData == null) return false; - ChangedLinesIterator changedLinesIterator = new ChangedLinesIterator(myChangedBlockData.getDiffChanges()); + List changes = myModel.getDiffChanges(); + if (changes == null) return false; + + ChangedLinesIterator changedLinesIterator = new ChangedLinesIterator(changes); int line = myNavigationContext.contextMatchCheck(changedLinesIterator); if (line == -1) { // this will work for the case, when spaces changes are ignored, and corresponding fragments are not reported as changed diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilder.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilder.java index caf00b209fb7..2289c16238c1 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilder.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilder.java @@ -32,7 +32,7 @@ class UnifiedFragmentBuilder { @NotNull private final Side myMasterSide; @NotNull private final StringBuilder myBuilder = new StringBuilder(); - @NotNull private final List myBlocks = new ArrayList<>(); + @NotNull private final List myChanges = new ArrayList<>(); @NotNull private final List myRanges = new ArrayList<>(); @NotNull private final LineNumberConvertor.Builder myConvertor1 = new LineNumberConvertor.Builder(); @NotNull private final LineNumberConvertor.Builder myConvertor2 = new LineNumberConvertor.Builder(); @@ -109,14 +109,7 @@ class UnifiedFragmentBuilder { linesAfter = totalLines; - int blockStartLine1 = linesBefore; - int blockEndLine1 = linesBetween; - int blockStartLine2 = linesBetween; - int blockEndLine2 = linesAfter; - - myBlocks.add(new ChangedBlock(new LineRange(blockStartLine1, blockEndLine1), - new LineRange(blockStartLine2, blockEndLine2), - fragment)); + myChanges.add(new UnifiedDiffChange(linesBefore, linesBetween, linesAfter, fragment)); lastProcessedLine1 = endLine1; lastProcessedLine2 = endLine2; @@ -190,8 +183,8 @@ class UnifiedFragmentBuilder { } @NotNull - public List getBlocks() { - return myBlocks; + public List getChanges() { + return myChanges; } @NotNull diff --git a/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt b/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt index fb21e26c28ad..8b1803f7635e 100644 --- a/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt +++ b/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt @@ -61,7 +61,7 @@ class UnifiedFragmentBuilderAutoTest : DiffTestCase() { val ignoreWhitespaces = policy !== ComparisonPolicy.DEFAULT val text = builder.text - val blocks = builder.blocks + val changes = builder.changes val convertor1 = builder.convertor1 val convertor2 = builder.convertor2 val changedLines = builder.changedLines @@ -93,16 +93,16 @@ class UnifiedFragmentBuilderAutoTest : DiffTestCase() { } // changed fragments and changed blocks should have same content - assertEquals(blocks.size, fragments.size) + assertEquals(changes.size, fragments.size) for (i in fragments.indices) { val fragment = fragments[i] - val block = blocks[i] + val block = changes[i] val fragment1 = sequence1.subSequence(fragment.startOffset1, fragment.endOffset1) val fragment2 = sequence2.subSequence(fragment.startOffset2, fragment.endOffset2) - val block1 = DiffUtil.getLinesContent(document, block.range1.start, block.range1.end) - val block2 = DiffUtil.getLinesContent(document, block.range2.start, block.range2.end) + val block1 = DiffUtil.getLinesContent(document, block.deletedRange.start, block.deletedRange.end) + val block2 = DiffUtil.getLinesContent(document, block.insertedRange.start, block.insertedRange.end) assertEqualsCharSequences(fragment1, block1, ignoreWhitespaces, true) assertEqualsCharSequences(fragment2, block2, ignoreWhitespaces, true) diff --git a/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderTest.kt b/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderTest.kt index 7596b94bce20..0cdc48fd81c9 100644 --- a/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderTest.kt +++ b/platform/diff-impl/tests/testSrc/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderTest.kt @@ -246,7 +246,7 @@ class UnifiedFragmentBuilderTest : DiffTestCase() { val actualText = processActualText(builder) val expectedMapping = processExpectedLineMapping(lineMapping) - val actualMapping = processActualLineMapping(builder.blocks, resultLineCount) + val actualMapping = processActualLineMapping(builder.changes, resultLineCount) val expectedChangedLines = processExpectedChangedLines(lineMapping) val actualChangedLines = processActualChangedLines(builder.changedLines) @@ -304,14 +304,14 @@ class UnifiedFragmentBuilderTest : DiffTestCase() { return LineMapping(leftSet, rightSet, unchangedSet) } - private fun processActualLineMapping(blocks: List, lineCount: Int): LineMapping { + private fun processActualLineMapping(blocks: List, lineCount: Int): LineMapping { val leftSet = BitSet() val rightSet = BitSet() val unchangedSet = BitSet() blocks.forEach { - leftSet.set(it.range1.start, it.range1.end) - rightSet.set(it.range2.start, it.range2.end) + leftSet.set(it.deletedRange.start, it.deletedRange.end) + rightSet.set(it.insertedRange.start, it.insertedRange.end) } unchangedSet.set(0, lineCount)