diff: refactor - extract model for UnifiedDiffViewer

GitOrigin-RevId: 810ba63bba58a945491f881ac461c7baf96bafd0
This commit is contained in:
Aleksey Pivovarov
2019-08-12 18:05:04 +03:00
committed by intellij-monorepo-bot
parent 55b8eada71
commit b81005465b
8 changed files with 355 additions and 325 deletions
@@ -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;
}
}
@@ -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<RangeHighlighter> myHighlighters = new ArrayList<>();
@NotNull private final List<MyGutterOperation> 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();
}
};
}
}
@@ -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<RangeHighlighter> myHighlighters = new ArrayList<>();
@NotNull private final List<MyGutterOperation> 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();
}
};
}
}
@@ -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<UnifiedDiffChangeUi> myPresentations = new ArrayList<>();
@NotNull private final List<RangeMarker> 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<UnifiedDiffChange> 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<UnifiedDiffChange> changes,
boolean isContentsEqual,
@NotNull List<RangeMarker> 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<UnifiedDiffChange> myDiffChanges;
@NotNull private final LineNumberConvertor myLineNumberConvertor1;
@NotNull private final LineNumberConvertor myLineNumberConvertor2;
ChangedBlockData(@NotNull List<UnifiedDiffChange> diffChanges,
@NotNull LineNumberConvertor lineNumberConvertor1,
@NotNull LineNumberConvertor lineNumberConvertor2) {
myDiffChanges = diffChanges;
myLineNumberConvertor1 = lineNumberConvertor1;
myLineNumberConvertor2 = lineNumberConvertor2;
}
@NotNull
public List<UnifiedDiffChange> getDiffChanges() {
return myDiffChanges;
}
@NotNull
public LineNumberConvertor getLineNumberConvertor(@NotNull Side side) {
return side.select(myLineNumberConvertor1, myLineNumberConvertor2);
}
}
}
@@ -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<? extends ChangedBlock> blocks,
@NotNull final List<UnifiedDiffChange> 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<UnifiedDiffChange> diffChanges = new ArrayList<>(blocks.size());
for (ChangedBlock block : blocks) {
diffChanges.add(new UnifiedDiffChange(this, block));
}
List<RangeMarker> 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<int[], Side> 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<UnifiedDiffChange> changes = myChangedBlockData.getDiffChanges();
if (changes.isEmpty()) return false;
List<UnifiedDiffChange> 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<UnifiedDiffChange> getSelectedChanges() {
if (myChangedBlockData == null) return Collections.emptyList();
final BitSet lines = DiffUtil.getSelectedLines(myEditor);
List<UnifiedDiffChange> changes = myChangedBlockData.getDiffChanges();
List<UnifiedDiffChange> 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<UnifiedDiffChange> 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<UnifiedDiffChange> 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<UnifiedDiffChange> myDiffChanges;
@NotNull private final List<RangeMarker> myGuardedRangeBlocks;
@NotNull private final LineNumberConvertor myLineNumberConvertor1;
@NotNull private final LineNumberConvertor myLineNumberConvertor2;
private final boolean myIsContentsEqual;
ChangedBlockData(@NotNull List<UnifiedDiffChange> diffChanges,
@NotNull List<RangeMarker> guarderRangeBlocks,
@NotNull LineNumberConvertor lineNumberConvertor1,
@NotNull LineNumberConvertor lineNumberConvertor2,
boolean isContentsEqual) {
myDiffChanges = diffChanges;
myGuardedRangeBlocks = guarderRangeBlocks;
myLineNumberConvertor1 = lineNumberConvertor1;
myLineNumberConvertor2 = lineNumberConvertor2;
myIsContentsEqual = isContentsEqual;
}
@NotNull
public List<UnifiedDiffChange> getDiffChanges() {
return myDiffChanges;
}
@NotNull
public List<RangeMarker> 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<UnifiedDiffChange> changes = myChangedBlockData.getDiffChanges();
List<UnifiedDiffChange> 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<UnifiedDiffChange> 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
@@ -32,7 +32,7 @@ class UnifiedFragmentBuilder {
@NotNull private final Side myMasterSide;
@NotNull private final StringBuilder myBuilder = new StringBuilder();
@NotNull private final List<ChangedBlock> myBlocks = new ArrayList<>();
@NotNull private final List<UnifiedDiffChange> myChanges = new ArrayList<>();
@NotNull private final List<HighlightRange> 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<ChangedBlock> getBlocks() {
return myBlocks;
public List<UnifiedDiffChange> getChanges() {
return myChanges;
}
@NotNull
@@ -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)
@@ -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<ChangedBlock>, lineCount: Int): LineMapping {
private fun processActualLineMapping(blocks: List<UnifiedDiffChange>, 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)