mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-138949 diff: allow to apply changes in oneside diff
This commit is contained in:
@@ -76,6 +76,11 @@ public enum Side {
|
||||
return isLeft() ? left : right;
|
||||
}
|
||||
|
||||
public boolean select(@NotNull boolean[] array) {
|
||||
assert array.length == 2;
|
||||
return array[myIndex];
|
||||
}
|
||||
|
||||
public int select(@NotNull int[] array) {
|
||||
assert array.length == 2;
|
||||
return array[myIndex];
|
||||
|
||||
@@ -18,12 +18,22 @@ package com.intellij.diff.tools.fragmented;
|
||||
import com.intellij.diff.fragments.DiffFragment;
|
||||
import com.intellij.diff.fragments.LineFragment;
|
||||
import com.intellij.diff.util.DiffDrawUtil;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.diff.util.TextDiffType;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.editor.markup.SeparatorPlacement;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.CalledWithWriteLock;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -42,6 +52,7 @@ public class OnesideDiffChange {
|
||||
@NotNull private final LineFragment myLineFragment;
|
||||
|
||||
@NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
|
||||
@NotNull private final List<MyGutterOperation> myOperations = new ArrayList<MyGutterOperation>();
|
||||
|
||||
public OnesideDiffChange(@NotNull OnesideDiffViewer viewer, @NotNull ChangedBlock block, boolean innerFragments) {
|
||||
myViewer = viewer;
|
||||
@@ -63,6 +74,11 @@ public class OnesideDiffChange {
|
||||
highlighter.dispose();
|
||||
}
|
||||
myHighlighters.clear();
|
||||
|
||||
for (MyGutterOperation operation : myOperations) {
|
||||
operation.dispose();
|
||||
}
|
||||
myOperations.clear();
|
||||
}
|
||||
|
||||
public void installHighlighter(boolean innerFragments) {
|
||||
@@ -74,6 +90,17 @@ public class OnesideDiffChange {
|
||||
else {
|
||||
doInstallHighlighterSimple();
|
||||
}
|
||||
doInstallActionHighlighters();
|
||||
}
|
||||
|
||||
private void doInstallActionHighlighters() {
|
||||
if (myViewer.getDocument(Side.LEFT) == null || myViewer.getDocument(Side.RIGHT) == null) return;
|
||||
|
||||
boolean leftEditable = myViewer.isEditable(Side.LEFT, false);
|
||||
boolean rightEditable = myViewer.isEditable(Side.RIGHT, false);
|
||||
|
||||
if (rightEditable) myOperations.add(createOperation(Side.LEFT, false));
|
||||
if (leftEditable) myOperations.add(createOperation(Side.RIGHT, rightEditable));
|
||||
}
|
||||
|
||||
private void doInstallHighlighterSimple() {
|
||||
@@ -160,4 +187,139 @@ public class OnesideDiffChange {
|
||||
private boolean hasDeletion() {
|
||||
return myStartOffset1 != myEndOffset1;
|
||||
}
|
||||
|
||||
@CalledWithWriteLock
|
||||
public void replaceChange(@NotNull final Side sourceSide) {
|
||||
Side affectedSide = sourceSide.other();
|
||||
if (!myViewer.isEditable(affectedSide, true)) return;
|
||||
|
||||
final Document document1 = myViewer.getDocument(Side.LEFT);
|
||||
final Document document2 = myViewer.getDocument(Side.RIGHT);
|
||||
assert document1 != null && document2 != null;
|
||||
|
||||
DiffUtil.applyModification(affectedSide.select(document1, document2),
|
||||
affectedSide.getStartLine(myLineFragment), affectedSide.getEndLine(myLineFragment),
|
||||
sourceSide.select(document1, document2),
|
||||
sourceSide.getStartLine(myLineFragment), sourceSide.getEndLine(myLineFragment));
|
||||
|
||||
destroyHighlighter();
|
||||
}
|
||||
|
||||
//
|
||||
// Gutter
|
||||
//
|
||||
|
||||
public void updateGutterActions() {
|
||||
for (MyGutterOperation operation : myOperations) {
|
||||
operation.update();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MyGutterOperation createOperation(@NotNull Side side, boolean secondAction) {
|
||||
int line = secondAction ? Math.min(myLine1 + 1, myLine2 - 1) : myLine1;
|
||||
int offset = myEditor.getDocument().getLineStartOffset(line);
|
||||
|
||||
RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(offset, offset,
|
||||
HighlighterLayer.ADDITIONAL_SYNTAX,
|
||||
null,
|
||||
HighlighterTargetArea.LINES_IN_RANGE);
|
||||
return new MyGutterOperation(side, 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.isEditable(mySide.other(), true)) return null;
|
||||
boolean bothEditable = myViewer.isEditable(mySide, true);
|
||||
|
||||
if (bothEditable) {
|
||||
if (mySide.isLeft()) {
|
||||
return createIconRenderer(mySide, "Apply Before", AllIcons.Diff.ArrowRight);
|
||||
}
|
||||
else {
|
||||
return createIconRenderer(mySide, "Apply After", AllIcons.Diff.Arrow);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mySide.isLeft()) {
|
||||
return createIconRenderer(mySide, "Revert", AllIcons.Diff.Remove);
|
||||
}
|
||||
else {
|
||||
return createIconRenderer(mySide, "Apply", AllIcons.Diff.Arrow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private GutterIconRenderer createIconRenderer(@NotNull final Side sourceSide,
|
||||
@NotNull final String tooltipText,
|
||||
@NotNull final Icon icon) {
|
||||
return new GutterIconRenderer() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public boolean isNavigateAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AnAction getClickAction() {
|
||||
return new DumbAwareAction() {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final Project project = e.getProject();
|
||||
final Document document = myViewer.getDocument(sourceSide.other());
|
||||
assert document != null;
|
||||
|
||||
DiffUtil.executeWriteCommand(document, project, "Replace change", new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
replaceChange(sourceSide);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTooltipText() {
|
||||
return tooltipText;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +93,17 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
|
||||
|
||||
@Nullable private ChangedBlockData myChangedBlockData;
|
||||
|
||||
private final boolean[] myForceReadOnlyFlags;
|
||||
private boolean myReadOnlyLockSet = false;
|
||||
|
||||
public OnesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
|
||||
super(context, (ContentDiffRequest)request);
|
||||
|
||||
myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
|
||||
myStatusPanel = new MyStatusPanel();
|
||||
|
||||
myForceReadOnlyFlags = checkForceReadOnly();
|
||||
|
||||
|
||||
List<DiffContent> contents = myRequest.getContents();
|
||||
myActualContent1 = contents.get(0) instanceof DocumentContent ? ((DocumentContent)contents.get(0)) : null;
|
||||
@@ -155,7 +160,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
|
||||
group.add(new MyIgnorePolicySettingAction());
|
||||
group.add(new MyHighlightPolicySettingAction());
|
||||
group.add(new MyToggleExpandByDefaultAction());
|
||||
group.add(new ReadOnlyLockAction());
|
||||
group.add(new MyReadOnlyLockAction());
|
||||
group.add(myEditorSettingsAction);
|
||||
|
||||
return group;
|
||||
@@ -557,6 +562,19 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
|
||||
return myStatusPanel;
|
||||
}
|
||||
|
||||
public boolean isEditable(@NotNull Side side, boolean respectReadOnlyLock) {
|
||||
if (myReadOnlyLockSet && respectReadOnlyLock) return false;
|
||||
if (side.select(myForceReadOnlyFlags)) return false;
|
||||
Document document = getDocument(side);
|
||||
return document != null && DiffUtil.canMakeWritable(document);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Document getDocument(@NotNull Side side) {
|
||||
DocumentContent content = side.select(myActualContent1, myActualContent2);
|
||||
return content != null ? content.getDocument() : null;
|
||||
}
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
@@ -731,6 +749,28 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private class MyReadOnlyLockAction extends ReadOnlyLockAction {
|
||||
public MyReadOnlyLockAction() {
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doApply(boolean readOnly) {
|
||||
myReadOnlyLockSet = readOnly;
|
||||
if (myChangedBlockData != null) {
|
||||
for (OnesideDiffChange onesideDiffChange : myChangedBlockData.getDiffChanges()) {
|
||||
onesideDiffChange.updateGutterActions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEdit() {
|
||||
return myActualContent1 != null && !myForceReadOnlyFlags[0] && DiffUtil.canMakeWritable(myActualContent1.getDocument()) ||
|
||||
myActualContent2 != null && !myForceReadOnlyFlags[1] && DiffUtil.canMakeWritable(myActualContent2.getDocument());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Scroll from annotate
|
||||
//
|
||||
|
||||
@@ -590,10 +590,10 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
}
|
||||
}
|
||||
|
||||
private class MyReadOnlyLockAction extends ReadOnlyLockAction {
|
||||
private class MyReadOnlyLockAction extends EditorReadOnlyLockAction {
|
||||
@Override
|
||||
public void setSelected(AnActionEvent e, boolean state) {
|
||||
super.setSelected(e, state);
|
||||
protected void doApply(boolean readOnly) {
|
||||
super.doApply(readOnly);
|
||||
for (SimpleDiffChange change : myDiffChanges) {
|
||||
change.updateGutterActions(true);
|
||||
}
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
//group.add(new MyHighlightPolicySettingAction()); // TODO
|
||||
group.add(new MyToggleExpandByDefaultAction());
|
||||
group.add(new ToggleAutoScrollAction());
|
||||
group.add(new ReadOnlyLockAction());
|
||||
group.add(new EditorReadOnlyLockAction());
|
||||
group.add(myEditorSettingsAction);
|
||||
|
||||
group.add(Separator.getInstance());
|
||||
|
||||
@@ -377,18 +377,13 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
|
||||
protected abstract void expandAll(boolean expand);
|
||||
}
|
||||
|
||||
protected class ReadOnlyLockAction extends ToggleAction implements DumbAware {
|
||||
private final List<? extends EditorEx> myEditableEditors;
|
||||
|
||||
protected abstract class ReadOnlyLockAction extends ToggleAction implements DumbAware {
|
||||
public ReadOnlyLockAction() {
|
||||
super("Disable editing", null, AllIcons.Nodes.Padlock);
|
||||
setEnabledInModalContext(true);
|
||||
myEditableEditors = ContainerUtil.filter(getEditors(), new Condition<EditorEx>() {
|
||||
@Override
|
||||
public boolean value(EditorEx editor) {
|
||||
return !editor.isViewer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
if (isVisible()) { // apply default state
|
||||
setSelected(null, isSelected(null));
|
||||
}
|
||||
@@ -412,14 +407,51 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
|
||||
@Override
|
||||
public void setSelected(AnActionEvent e, boolean state) {
|
||||
myContext.putUserData(READ_ONLY_LOCK_KEY, state);
|
||||
for (EditorEx editor : myEditableEditors) {
|
||||
editor.setViewer(state);
|
||||
}
|
||||
doApply(state);
|
||||
}
|
||||
|
||||
private boolean isVisible() {
|
||||
return !myEditableEditors.isEmpty() && myContext.getUserData(DiffUserDataKeysEx.SHOW_READ_ONLY_LOCK) == Boolean.TRUE;
|
||||
return myContext.getUserData(DiffUserDataKeysEx.SHOW_READ_ONLY_LOCK) == Boolean.TRUE && canEdit();
|
||||
}
|
||||
|
||||
protected abstract void doApply(boolean readOnly);
|
||||
|
||||
protected abstract boolean canEdit();
|
||||
}
|
||||
|
||||
protected class EditorReadOnlyLockAction extends ReadOnlyLockAction {
|
||||
private final List<? extends EditorEx> myEditableEditors;
|
||||
|
||||
public EditorReadOnlyLockAction() {
|
||||
this(getEditableEditors(getEditors()));
|
||||
}
|
||||
|
||||
public EditorReadOnlyLockAction(@NotNull List<? extends EditorEx> editableEditors) {
|
||||
myEditableEditors = editableEditors;
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doApply(boolean readOnly) {
|
||||
for (EditorEx editor : myEditableEditors) {
|
||||
editor.setViewer(readOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEdit() {
|
||||
return !myEditableEditors.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static List<? extends EditorEx> getEditableEditors(@NotNull List<? extends EditorEx> editors) {
|
||||
return ContainerUtil.filter(editors, new Condition<EditorEx>() {
|
||||
@Override
|
||||
public boolean value(EditorEx editor) {
|
||||
return !editor.isViewer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final class MyEditorMouseListener extends EditorPopupHandler {
|
||||
|
||||
Reference in New Issue
Block a user