mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
patch: apply-somehow - rework dialog
* make it more similar to the merge conflict resolve dialog * allow editing in diff viewer
This commit is contained in:
committed by
Aleksey Pivovarov
parent
14f8e50af2
commit
6e3d8650db
@@ -53,6 +53,9 @@ public abstract class DiffContentFactory {
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@NotNull String text, @Nullable VirtualFile highlightFile);
|
||||
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@NotNull String text, @Nullable DocumentContent referent);
|
||||
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@Nullable Project project, @NotNull Document document);
|
||||
|
||||
@@ -62,6 +65,9 @@ public abstract class DiffContentFactory {
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@Nullable Project project, @NotNull Document document, @Nullable VirtualFile file);
|
||||
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@NotNull Document document, @Nullable DocumentContent referent);
|
||||
|
||||
@NotNull
|
||||
public abstract DiffContent create(@Nullable Project project, @NotNull VirtualFile file);
|
||||
|
||||
|
||||
@@ -81,6 +81,20 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
return createImpl(text, highlightFile != null ? highlightFile.getFileType() : null, highlightFile, null, true, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DocumentContent create(@NotNull String text, @Nullable DocumentContent referent) {
|
||||
if (referent == null) return create(text);
|
||||
return createImpl(text, referent.getContentType(), referent.getHighlightFile(), null, false, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DocumentContent create(@NotNull Document document, @Nullable DocumentContent referent) {
|
||||
if (referent == null) return new DocumentContentImpl(document);
|
||||
return new DocumentContentImpl(document, referent.getContentType(), referent.getHighlightFile(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentContent create(@Nullable Project project, @NotNull Document document) {
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
abstract class MergeModelBase<S extends MergeModelBase.State> implements Disposable {
|
||||
public abstract class MergeModelBase<S extends MergeModelBase.State> implements Disposable {
|
||||
public static final Logger LOG = Logger.getInstance(MergeModelBase.class);
|
||||
|
||||
@Nullable private final Project myProject;
|
||||
|
||||
@@ -40,9 +40,6 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy;
|
||||
import com.intellij.openapi.command.undo.DocumentReference;
|
||||
import com.intellij.openapi.command.undo.DocumentReferenceManager;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -54,12 +51,8 @@ import com.intellij.openapi.progress.util.ProgressWindow;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.ui.HyperlinkAdapter;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -68,8 +61,6 @@ import gnu.trove.TIntArrayList;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.*;
|
||||
@@ -300,12 +291,7 @@ public class TextMergeViewer implements MergeTool.MergeViewer {
|
||||
|
||||
DiffUtil.executeWriteCommand(outputDocument, getProject(), "Init merge content", () -> {
|
||||
outputDocument.setText(baseDocument.getCharsSequence());
|
||||
|
||||
UndoManager undoManager = getProject() != null ? UndoManager.getInstance(getProject()) : UndoManager.getGlobalInstance();
|
||||
if (undoManager != null) {
|
||||
DocumentReference ref = DocumentReferenceManager.getInstance().create(outputDocument);
|
||||
undoManager.nonundoableActionPerformed(ref, false);
|
||||
}
|
||||
DiffUtil.putNonundoableOperation(getProject(), outputDocument);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -581,26 +567,16 @@ public class TextMergeViewer implements MergeTool.MergeViewer {
|
||||
LOG.assertTrue(getFirstUnresolvedChange(true, null) == null);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (isDisposed()) return;
|
||||
String message = "All changes have been processed.<br><a href=\"\">Save changes and finish merging</a>";
|
||||
HyperlinkListener listener = new HyperlinkAdapter() {
|
||||
@Override
|
||||
protected void hyperlinkActivated(HyperlinkEvent e) {
|
||||
if (isDisposed()) return;
|
||||
destroyChangedBlocks();
|
||||
myMergeContext.finishMerge(MergeResult.RESOLVED);
|
||||
}
|
||||
};
|
||||
|
||||
JComponent component = getEditor().getComponent();
|
||||
Point point = new Point(component.getWidth() / 2, JBUI.scale(5));
|
||||
Color bgColor = MessageType.INFO.getPopupBackground();
|
||||
RelativePoint point = new RelativePoint(component, new Point(component.getWidth() / 2, JBUI.scale(5)));
|
||||
|
||||
Balloon balloon = JBPopupFactory.getInstance()
|
||||
.createHtmlTextBalloonBuilder(message, null, bgColor, listener)
|
||||
.setAnimationCycle(200)
|
||||
.createBalloon();
|
||||
balloon.show(new RelativePoint(component, point), Balloon.Position.below);
|
||||
Disposer.register(this, balloon);
|
||||
String message = DiffBundle.message("merge.all.changes.processed.message.text");
|
||||
DiffUtil.showSuccessPopup(message, point, this, () -> {
|
||||
if (isDisposed()) return;
|
||||
destroyChangedBlocks();
|
||||
myMergeContext.finishMerge(MergeResult.RESOLVED);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.intellij.diff.tools.fragmented;
|
||||
|
||||
import com.intellij.diff.fragments.DiffFragment;
|
||||
import com.intellij.diff.fragments.LineFragment;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffUtil.UpdatedLineRange;
|
||||
@@ -77,12 +76,7 @@ public class UnifiedDiffChange {
|
||||
private void installHighlighter(@NotNull LineRange deleted, @NotNull LineRange inserted) {
|
||||
assert myHighlighters.isEmpty();
|
||||
|
||||
if (myLineFragment.getInnerFragments() != null) {
|
||||
doInstallHighlighterWithInner(deleted, inserted);
|
||||
}
|
||||
else {
|
||||
doInstallHighlighterSimple(deleted, inserted);
|
||||
}
|
||||
doInstallHighlighters(deleted, inserted);
|
||||
doInstallActionHighlighters();
|
||||
}
|
||||
|
||||
@@ -99,35 +93,8 @@ public class UnifiedDiffChange {
|
||||
}
|
||||
}
|
||||
|
||||
private void doInstallHighlighterSimple(@NotNull LineRange deleted, @NotNull LineRange inserted) {
|
||||
createLineHighlighters(deleted, inserted, false);
|
||||
}
|
||||
|
||||
private void doInstallHighlighterWithInner(@NotNull LineRange deleted, @NotNull LineRange inserted) {
|
||||
List<DiffFragment> innerFragments = myLineFragment.getInnerFragments();
|
||||
assert innerFragments != null;
|
||||
|
||||
int deletedStartOffset = myEditor.getDocument().getLineStartOffset(deleted.start);
|
||||
int insertedStartOffset = myEditor.getDocument().getLineStartOffset(inserted.start);
|
||||
|
||||
createLineHighlighters(deleted, inserted, true);
|
||||
|
||||
for (DiffFragment fragment : innerFragments) {
|
||||
createInlineHighlighter(TextDiffType.DELETED,
|
||||
deletedStartOffset + fragment.getStartOffset1(),
|
||||
deletedStartOffset + fragment.getEndOffset1());
|
||||
createInlineHighlighter(TextDiffType.INSERTED,
|
||||
insertedStartOffset + fragment.getStartOffset2(),
|
||||
insertedStartOffset + fragment.getEndOffset2());
|
||||
}
|
||||
}
|
||||
|
||||
private void createLineHighlighters(@NotNull LineRange deleted, @NotNull LineRange inserted, boolean ignored) {
|
||||
myHighlighters.addAll(DiffDrawUtil.createUnifiedChunkHighlighters(myEditor, deleted, inserted, ignored));
|
||||
}
|
||||
|
||||
private void createInlineHighlighter(@NotNull TextDiffType type, int start, int end) {
|
||||
myHighlighters.addAll(DiffDrawUtil.createInlineHighlighter(myEditor, start, end, type));
|
||||
private void doInstallHighlighters(@NotNull LineRange deleted, @NotNull LineRange inserted) {
|
||||
myHighlighters.addAll(DiffDrawUtil.createUnifiedChunkHighlighters(myEditor, deleted, inserted, myLineFragment.getInnerFragments()));
|
||||
}
|
||||
|
||||
public int getLine1() {
|
||||
|
||||
@@ -66,19 +66,20 @@ public class TextEditorHolder extends EditorHolder {
|
||||
// Build
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public static TextEditorHolder create(@Nullable Project project, @NotNull DocumentContent content) {
|
||||
EditorEx editor = DiffUtil.createEditor(content.getDocument(), project, false, true);
|
||||
DiffUtil.configureEditor(editor, content, project);
|
||||
return new TextEditorHolder(editor);
|
||||
}
|
||||
|
||||
public static class TextEditorHolderFactory extends EditorHolderFactory<TextEditorHolder> {
|
||||
public static TextEditorHolderFactory INSTANCE = new TextEditorHolderFactory();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TextEditorHolder create(@NotNull DiffContent content, @NotNull DiffContext context) {
|
||||
if (!(content instanceof DocumentContent)) throw new IllegalArgumentException(content.toString());
|
||||
Project project = context.getProject();
|
||||
DocumentContent documentContent = (DocumentContent)content;
|
||||
|
||||
EditorEx editor = DiffUtil.createEditor(documentContent.getDocument(), project, false, true);
|
||||
DiffUtil.configureEditor(editor, documentContent, project);
|
||||
return new TextEditorHolder(editor);
|
||||
return TextEditorHolder.create(context.getProject(), (DocumentContent)content);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.diff.util;
|
||||
|
||||
import com.intellij.diff.fragments.DiffFragment;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
@@ -242,7 +243,9 @@ public class DiffDrawUtil {
|
||||
public static List<RangeHighlighter> createUnifiedChunkHighlighters(@NotNull Editor editor,
|
||||
@NotNull LineRange deleted,
|
||||
@NotNull LineRange inserted,
|
||||
boolean ignored) {
|
||||
@Nullable List<DiffFragment> innerFragments) {
|
||||
boolean ignored = innerFragments != null;
|
||||
|
||||
List<RangeHighlighter> list = new ArrayList<>();
|
||||
if (!inserted.isEmpty() && !deleted.isEmpty()) {
|
||||
list.addAll(createLineMarker(editor, deleted.start, TextDiffType.DELETED, SeparatorPlacement.TOP));
|
||||
@@ -260,6 +263,22 @@ public class DiffDrawUtil {
|
||||
list.addAll(createHighlighter(editor, deleted.start, deleted.end, TextDiffType.DELETED, ignored));
|
||||
list.addAll(createLineMarker(editor, deleted.end - 1, TextDiffType.DELETED, SeparatorPlacement.BOTTOM));
|
||||
}
|
||||
|
||||
if (innerFragments != null) {
|
||||
int deletedStartOffset = editor.getDocument().getLineStartOffset(deleted.start);
|
||||
int insertedStartOffset = editor.getDocument().getLineStartOffset(inserted.start);
|
||||
|
||||
for (DiffFragment fragment : innerFragments) {
|
||||
int deletedWordStart = deletedStartOffset + fragment.getStartOffset1();
|
||||
int deletedWordEnd = deletedStartOffset + fragment.getEndOffset1();
|
||||
list.addAll(createInlineHighlighter(editor, deletedWordStart, deletedWordEnd, TextDiffType.DELETED));
|
||||
|
||||
int insertedWordStart = insertedStartOffset + fragment.getStartOffset2();
|
||||
int insertedWordEnd = insertedStartOffset + fragment.getEndOffset2();
|
||||
list.addAll(createInlineHighlighter(editor, insertedWordStart, insertedWordEnd, TextDiffType.INSERTED));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,15 @@ import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.impl.LaterInvocator;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy;
|
||||
import com.intellij.openapi.command.undo.DocumentReference;
|
||||
import com.intellij.openapi.command.undo.DocumentReferenceManager;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.diff.impl.GenericDataProvider;
|
||||
@@ -60,13 +64,17 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType;
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.ui.DialogWrapperDialog;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.WindowWrapper;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -75,11 +83,10 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.intellij.openapi.wm.impl.IdeFrameImpl;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.ScreenUtil;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.LineSeparator;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -90,11 +97,11 @@ import org.jetbrains.annotations.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import java.awt.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class DiffUtil {
|
||||
@@ -129,7 +136,7 @@ public class DiffUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static EditorHighlighter createEditorHighlighter(@Nullable Project project, @NotNull DocumentContent content) {
|
||||
private static EditorHighlighter createEditorHighlighter(@Nullable Project project, @NotNull DocumentContent content) {
|
||||
FileType type = content.getContentType();
|
||||
VirtualFile file = content.getHighlightFile();
|
||||
Language language = content.getUserData(DiffUserDataKeys.LANGUAGE);
|
||||
@@ -139,18 +146,19 @@ public class DiffUtil {
|
||||
SyntaxHighlighter syntaxHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(language, project, file);
|
||||
return highlighterFactory.createEditorHighlighter(syntaxHighlighter, EditorColorsManager.getInstance().getGlobalScheme());
|
||||
}
|
||||
else if ((file != null && file.getFileType() == type) || file instanceof LightVirtualFile) {
|
||||
return highlighterFactory.createEditorHighlighter(project, file);
|
||||
if (file != null) {
|
||||
if ((type == null || type == PlainTextFileType.INSTANCE) || file.getFileType() == type || file instanceof LightVirtualFile) {
|
||||
return highlighterFactory.createEditorHighlighter(project, file);
|
||||
}
|
||||
}
|
||||
if (type != null) {
|
||||
return highlighterFactory.createEditorHighlighter(project, type);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static EditorHighlighter createEmptyEditorHighlighter() {
|
||||
private static EditorHighlighter createEmptyEditorHighlighter() {
|
||||
return new EmptyEditorHighlighter(EditorColorsManager.getInstance().getGlobalScheme().getAttributes(HighlighterColors.TEXT));
|
||||
}
|
||||
|
||||
@@ -366,6 +374,30 @@ public class DiffUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void showSuccessPopup(@NotNull String message,
|
||||
@NotNull RelativePoint point,
|
||||
@NotNull Disposable disposable,
|
||||
@Nullable Runnable hyperlinkHandler) {
|
||||
HyperlinkListener listener = null;
|
||||
if (hyperlinkHandler != null) {
|
||||
listener = new HyperlinkAdapter() {
|
||||
@Override
|
||||
protected void hyperlinkActivated(HyperlinkEvent e) {
|
||||
hyperlinkHandler.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Color bgColor = MessageType.INFO.getPopupBackground();
|
||||
|
||||
Balloon balloon = JBPopupFactory.getInstance()
|
||||
.createHtmlTextBalloonBuilder(message, null, bgColor, listener)
|
||||
.setAnimationCycle(200)
|
||||
.createBalloon();
|
||||
balloon.show(point, Balloon.Position.below);
|
||||
Disposer.register(disposable, balloon);
|
||||
}
|
||||
|
||||
//
|
||||
// Titles
|
||||
//
|
||||
@@ -441,7 +473,7 @@ public class DiffUtil {
|
||||
|
||||
@NotNull
|
||||
public static JComponent createTitle(@NotNull String title) {
|
||||
return createTitle(title, null, null, true);
|
||||
return createTitle(title, null, null, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -622,6 +654,31 @@ public class DiffUtil {
|
||||
return ComparisonManager.getInstance().isEquals(chunk1, chunk2, comparisonPolicy);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> int[] getSortedIndexes(@NotNull List<T> values, @NotNull Comparator<T> comparator) {
|
||||
final List<Integer> indexes = new ArrayList<>(values.size());
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
indexes.add(i);
|
||||
}
|
||||
|
||||
ContainerUtil.sort(indexes, (i1, i2) -> {
|
||||
T val1 = values.get(indexes.get(i1));
|
||||
T val2 = values.get(indexes.get(i2));
|
||||
return comparator.compare(val1, val2);
|
||||
});
|
||||
|
||||
return ArrayUtil.toIntArray(indexes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> int[] invertIndexes(@NotNull int[] indexes) {
|
||||
int[] inverted = new int[indexes.length];
|
||||
for (int i = 0; i < indexes.length; i++) {
|
||||
inverted[indexes[i]] = i;
|
||||
}
|
||||
return inverted;
|
||||
}
|
||||
|
||||
//
|
||||
// Document modification
|
||||
//
|
||||
@@ -814,12 +871,11 @@ public class DiffUtil {
|
||||
|
||||
@NotNull
|
||||
public static UpdatedLineRange updateRangeOnModification(int start, int end, int changeStart, int changeEnd, int shift) {
|
||||
return updateRangeOnModification(start, end, changeStart, changeEnd, shift, false, false);
|
||||
return updateRangeOnModification(start, end, changeStart, changeEnd, shift, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static UpdatedLineRange updateRangeOnModification(int start, int end, int changeStart, int changeEnd, int shift,
|
||||
boolean greedy, boolean strict) {
|
||||
public static UpdatedLineRange updateRangeOnModification(int start, int end, int changeStart, int changeEnd, int shift, boolean greedy) {
|
||||
if (end <= changeStart) { // change before
|
||||
return new UpdatedLineRange(start, end, false);
|
||||
}
|
||||
@@ -828,7 +884,7 @@ public class DiffUtil {
|
||||
}
|
||||
|
||||
if (start <= changeStart && end >= changeEnd) { // change inside
|
||||
return new UpdatedLineRange(start, end + shift, strict);
|
||||
return new UpdatedLineRange(start, end + shift, false);
|
||||
}
|
||||
|
||||
// range is damaged. We don't know new boundaries.
|
||||
@@ -869,28 +925,29 @@ public class DiffUtil {
|
||||
public static TextDiffType getLineDiffType(@NotNull LineFragment fragment) {
|
||||
boolean left = fragment.getStartLine1() != fragment.getEndLine1();
|
||||
boolean right = fragment.getStartLine2() != fragment.getEndLine2();
|
||||
return getType(left, right);
|
||||
return getDiffType(left, right);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TextDiffType getDiffType(@NotNull DiffFragment fragment) {
|
||||
boolean left = fragment.getEndOffset1() != fragment.getStartOffset1();
|
||||
boolean right = fragment.getEndOffset2() != fragment.getStartOffset2();
|
||||
return getType(left, right);
|
||||
return getDiffType(left, right);
|
||||
}
|
||||
|
||||
private static TextDiffType getType(boolean left, boolean right) {
|
||||
if (left && right) {
|
||||
@NotNull
|
||||
public static TextDiffType getDiffType(boolean hasDeleted, boolean hasInserted) {
|
||||
if (hasDeleted && hasInserted) {
|
||||
return TextDiffType.MODIFIED;
|
||||
}
|
||||
else if (left) {
|
||||
else if (hasDeleted) {
|
||||
return TextDiffType.DELETED;
|
||||
}
|
||||
else if (right) {
|
||||
else if (hasInserted) {
|
||||
return TextDiffType.INSERTED;
|
||||
}
|
||||
else {
|
||||
LOG.error("DiffFragment should not be empty");
|
||||
LOG.error("Diff fragment should not be empty");
|
||||
return TextDiffType.MODIFIED;
|
||||
}
|
||||
}
|
||||
@@ -990,6 +1047,14 @@ public class DiffUtil {
|
||||
return !ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(file).hasReadonlyFiles();
|
||||
}
|
||||
|
||||
public static void putNonundoableOperation(@Nullable Project project, @NotNull Document document) {
|
||||
UndoManager undoManager = project != null ? UndoManager.getInstance(project) : UndoManager.getGlobalInstance();
|
||||
if (undoManager != null) {
|
||||
DocumentReference ref = DocumentReferenceManager.getInstance().create(document);
|
||||
undoManager.nonundoableActionPerformed(ref, false);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Windows
|
||||
//
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -106,12 +107,12 @@ public class TextDiffTypeFactory {
|
||||
if (editor instanceof EditorEx) {
|
||||
Color fg = attributes.getBackgroundColor();
|
||||
Color bg = ((EditorEx)editor).getBackgroundColor();
|
||||
return getMiddleColor(fg, bg);
|
||||
return ColorUtil.mix(fg, bg, MIDDLE_COLOR_FACTOR);
|
||||
}
|
||||
else {
|
||||
Color fg = attributes.getBackgroundColor();
|
||||
Color bg = EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground();
|
||||
return getMiddleColor(fg, bg);
|
||||
return ColorUtil.mix(fg, bg, MIDDLE_COLOR_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,14 +137,6 @@ public class TextDiffTypeFactory {
|
||||
|
||||
@NotNull
|
||||
public static Color getMiddleColor(@NotNull Color fg, @NotNull Color bg) {
|
||||
int red = avg(fg.getRed(), bg.getRed(), MIDDLE_COLOR_FACTOR);
|
||||
int green = avg(fg.getGreen(), bg.getGreen(), MIDDLE_COLOR_FACTOR);
|
||||
int blue = avg(fg.getBlue(), bg.getBlue(), MIDDLE_COLOR_FACTOR);
|
||||
//noinspection UseJBColor
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
private static int avg(int fg, int bg, double factor) {
|
||||
return (int)(fg + Math.round(factor * (bg - fg)));
|
||||
return ColorUtil.mix(fg, bg, MIDDLE_COLOR_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ merge.dialog.all.conflicts.resolved.message.text=All conflicts resolved
|
||||
merge.statistics.message={0, choice, 0#No changes|1#One change|2#{0, number} changes}. {1, choice, 0#No conflicts|1#One conflict|2#{0, number} conflicts}
|
||||
merge.dialog.exit.without.applying.changes.confirmation.message=Are you sure you want to exit without applying changes?
|
||||
cancel.visual.merge.dialog.title=Cancel Visual Merge
|
||||
apply.somehow.status.message.all.applied=All hunks resolved
|
||||
apply.somehow.status.message.already.applied={0, number} {0, choice, 1#hunk is|2#hunks are} already applied
|
||||
apply.somehow.status.message.cant.apply=Can''t apply {0, number} {0, choice, 1#hunk|2#hunks}
|
||||
apply.somehow.status.message.cant.apply.some=Can''t apply {0, number} of {1, number} hunks
|
||||
@@ -77,6 +78,8 @@ merge.dialog.apply.partially.resolved.changes.confirmation.message=There {0, cho
|
||||
apply.partially.resolved.merge.dialog.title=Apply Changes
|
||||
merge.dialog.resolve.side.with.discard.title=Accept Changes
|
||||
merge.dialog.resolve.side.with.discard.message=Are you sure you want to discard your changes and accept {0, choice, 0#left|1#right} side?
|
||||
merge.all.changes.processed.message.text=All changes have been processed.<br><a href="">Save changes and finish merging</a>
|
||||
apply.patch.all.changes.processed.message.text=All hunks have been processed.<br><a href=\"\">Save changes and finish resolve</a>
|
||||
merge.all.changes.have.processed.save.and.finish.confirmation.text=All changes have been processed.\nWould you like to save changes and finish merging?
|
||||
all.changes.processed.dialog.title=All Changes Processed
|
||||
merge.save.and.finish.button=Save and &Finish
|
||||
|
||||
+8
-4
@@ -93,7 +93,7 @@ public class PatchDiffRequestFactory {
|
||||
applier.execute();
|
||||
|
||||
final AppliedTextPatch appliedTextPatch = AppliedTextPatch.create(applier.getAppliedInfo());
|
||||
return createBadDiffRequest(file, localContent, appliedTextPatch, null, null, null, null);
|
||||
return createBadDiffRequest(project, file, localContent, appliedTextPatch, null, null, null, null);
|
||||
}
|
||||
else {
|
||||
String localContent = texts.getLocal().toString();
|
||||
@@ -133,7 +133,8 @@ public class PatchDiffRequestFactory {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DiffRequest createBadDiffRequest(@Nullable VirtualFile file,
|
||||
public static DiffRequest createBadDiffRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile file,
|
||||
@NotNull String localContent,
|
||||
@NotNull AppliedTextPatch textPatch,
|
||||
@Nullable String windowTitle,
|
||||
@@ -145,7 +146,9 @@ public class PatchDiffRequestFactory {
|
||||
if (resultTitle == null) resultTitle = VcsBundle.message("patch.apply.conflict.patched.somehow.version");
|
||||
if (patchTitle == null) patchTitle = VcsBundle.message("patch.apply.conflict.patch");
|
||||
|
||||
return new ApplyPatchDiffRequest(textPatch, localContent, file, windowTitle, localTitle, resultTitle, patchTitle);
|
||||
DocumentContent resultContent = DiffContentFactory.getInstance().createDocument(project, file);
|
||||
if (resultContent == null) resultContent = DiffContentFactory.getInstance().create(localContent, file);
|
||||
return new ApplyPatchDiffRequest(resultContent, textPatch, localContent, windowTitle, localTitle, resultTitle, patchTitle);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -219,7 +222,8 @@ public class PatchDiffRequestFactory {
|
||||
if (resultTitle == null) resultTitle = VcsBundle.message("patch.apply.conflict.patched.somehow.version");
|
||||
if (patchTitle == null) patchTitle = VcsBundle.message("patch.apply.conflict.patch");
|
||||
|
||||
return new ApplyPatchMergeRequest(project, document, textPatch, localContent,
|
||||
DocumentContent resultContent = DiffContentFactory.getInstance().create(project, document, file);
|
||||
return new ApplyPatchMergeRequest(project, resultContent, textPatch, localContent,
|
||||
windowTitle, localTitle, resultTitle, patchTitle, callback);
|
||||
}
|
||||
|
||||
|
||||
+248
-94
@@ -17,27 +17,34 @@ package com.intellij.openapi.vcs.changes.patch.tool;
|
||||
|
||||
import com.intellij.diff.comparison.ByWord;
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.comparison.DiffTooBigException;
|
||||
import com.intellij.diff.fragments.DiffFragment;
|
||||
import com.intellij.diff.tools.util.SyncScrollSupport;
|
||||
import com.intellij.diff.merge.MergeModelBase;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.MarkupModelEx;
|
||||
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.progress.DumbProgressIndicator;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch.HunkStatus;
|
||||
import com.intellij.openapi.vcs.ex.LineStatusMarkerRenderer;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.PairConsumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
@@ -45,42 +52,109 @@ import java.util.List;
|
||||
|
||||
class ApplyPatchChange {
|
||||
@NotNull private final ApplyPatchViewer myViewer;
|
||||
private final int myIndex; // index in myModelChanges
|
||||
|
||||
@NotNull private final LineRange myPatchDeletionRange;
|
||||
@NotNull private final LineRange myPatchInsertionRange;
|
||||
@NotNull private final AppliedTextPatch.HunkStatus myStatus;
|
||||
@NotNull private final HunkStatus myStatus;
|
||||
|
||||
@Nullable private final List<DiffFragment> myPatchInnerDifferences;
|
||||
@NotNull private final List<MyGutterOperation> myOperations = new ArrayList<>();
|
||||
|
||||
@NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<>();
|
||||
|
||||
@Nullable private LineRange myAppliedTo;
|
||||
private boolean myIsValid = true;
|
||||
private boolean myResolved;
|
||||
|
||||
public ApplyPatchChange(@NotNull ApplyPatchViewer viewer, @NotNull PatchChangeBuilder.Hunk hunk) {
|
||||
public ApplyPatchChange(@NotNull PatchChangeBuilder.Hunk hunk, int index, @NotNull ApplyPatchViewer viewer) {
|
||||
myIndex = index;
|
||||
myViewer = viewer;
|
||||
myPatchDeletionRange = hunk.getPatchDeletionRange();
|
||||
myPatchInsertionRange = hunk.getPatchInsertionRange();
|
||||
myStatus = hunk.getStatus();
|
||||
myAppliedTo = hunk.getAppliedToLines();
|
||||
|
||||
installHighlighters();
|
||||
installPersistentHighlighters();
|
||||
myPatchInnerDifferences = calcPatchInnerDifferences(hunk, viewer);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<DiffFragment> calcPatchInnerDifferences(@NotNull PatchChangeBuilder.Hunk hunk,
|
||||
@NotNull ApplyPatchViewer viewer) {
|
||||
LineRange deletionRange = hunk.getPatchDeletionRange();
|
||||
LineRange insertionRange = hunk.getPatchInsertionRange();
|
||||
|
||||
if (deletionRange.isEmpty() || insertionRange.isEmpty()) return null;
|
||||
|
||||
try {
|
||||
DocumentEx patchDocument = viewer.getPatchEditor().getDocument();
|
||||
CharSequence deleted = DiffUtil.getLinesContent(patchDocument, deletionRange.start, deletionRange.end);
|
||||
CharSequence inserted = DiffUtil.getLinesContent(patchDocument, insertionRange.start, insertionRange.end);
|
||||
|
||||
return ByWord.compare(deleted, inserted, ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE);
|
||||
}
|
||||
catch (DiffTooBigException ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void reinstallHighlighters() {
|
||||
destroyHighlighters();
|
||||
installHighlighters();
|
||||
|
||||
myViewer.repaintDivider();
|
||||
}
|
||||
|
||||
private void installHighlighters() {
|
||||
createStatusHighlighter(myViewer.getPatchEditor(), myPatchDeletionRange.start, myPatchInsertionRange.end);
|
||||
createResultHighlighters();
|
||||
createPatchHighlighters();
|
||||
createStatusHighlighter();
|
||||
createOperations();
|
||||
}
|
||||
|
||||
if (myAppliedTo != null) {
|
||||
EditorEx resultEditor = myViewer.getResultEditor();
|
||||
createStatusHighlighter(resultEditor, myAppliedTo.start, myAppliedTo.end);
|
||||
private void createPatchHighlighters() {
|
||||
EditorEx patchEditor = myViewer.getPatchEditor();
|
||||
myHighlighters.addAll(DiffDrawUtil.createUnifiedChunkHighlighters(patchEditor, myPatchDeletionRange, myPatchInsertionRange,
|
||||
myPatchInnerDifferences));
|
||||
}
|
||||
|
||||
myHighlighters.addAll(DiffDrawUtil.createLineMarker(resultEditor, myAppliedTo.start, myAppliedTo.end,
|
||||
TextDiffType.MODIFIED, true));
|
||||
private void createResultHighlighters() {
|
||||
LineRange resultRange = getResultRange();
|
||||
if (resultRange == null) return;
|
||||
EditorEx editor = myViewer.getResultEditor();
|
||||
|
||||
int startLine = resultRange.start;
|
||||
int endLine = resultRange.end;
|
||||
|
||||
TextDiffType type = getDiffType();
|
||||
boolean resolved = isRangeApplied();
|
||||
|
||||
myHighlighters.addAll(DiffDrawUtil.createHighlighter(editor, startLine, endLine, type, false, resolved, false));
|
||||
myHighlighters.addAll(DiffDrawUtil.createLineMarker(editor, startLine, endLine, type, resolved));
|
||||
}
|
||||
|
||||
private void createStatusHighlighter() {
|
||||
int line1 = myPatchDeletionRange.start;
|
||||
int line2 = myPatchInsertionRange.end;
|
||||
|
||||
Color color = getStatusColor();
|
||||
if (isResolved()) {
|
||||
color = ColorUtil.mix(color, myViewer.getPatchEditor().getGutterComponentEx().getBackground(), 0.6f);
|
||||
}
|
||||
|
||||
String tooltip = getStatusText();
|
||||
|
||||
EditorEx patchEditor = myViewer.getPatchEditor();
|
||||
Document document = patchEditor.getDocument();
|
||||
MarkupModelEx markupModel = patchEditor.getMarkupModel();
|
||||
TextRange textRange = DiffUtil.getLinesRange(document, line1, line2);
|
||||
|
||||
RangeHighlighter highlighter = markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(),
|
||||
HighlighterLayer.LAST, null, HighlighterTargetArea.LINES_IN_RANGE);
|
||||
|
||||
PairConsumer<Editor, MouseEvent> clickHandler = getResultRange() != null ?
|
||||
(e, event) -> myViewer.scrollToChange(this, Side.RIGHT, false) :
|
||||
null;
|
||||
highlighter.setLineMarkerRenderer(LineStatusMarkerRenderer.createRenderer(line1, line2, color, tooltip, clickHandler));
|
||||
|
||||
myHighlighters.add(highlighter);
|
||||
}
|
||||
|
||||
private void destroyHighlighters() {
|
||||
@@ -88,42 +162,71 @@ class ApplyPatchChange {
|
||||
highlighter.dispose();
|
||||
}
|
||||
myHighlighters.clear();
|
||||
|
||||
for (MyGutterOperation operation : myOperations) {
|
||||
operation.dispose();
|
||||
}
|
||||
myOperations.clear();
|
||||
}
|
||||
|
||||
private void createStatusHighlighter(@NotNull EditorEx editor, int line1, int line2) {
|
||||
Color color = getStatusColor();
|
||||
String tooltip = getStatusText();
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
Document document = editor.getDocument();
|
||||
MarkupModelEx markupModel = editor.getMarkupModel();
|
||||
TextRange textRange = DiffUtil.getLinesRange(document, line1, line2);
|
||||
|
||||
RangeHighlighter highlighter = markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(),
|
||||
HighlighterLayer.LAST, null, HighlighterTargetArea.LINES_IN_RANGE);
|
||||
|
||||
PairConsumer<Editor, MouseEvent> clickHandler = myAppliedTo != null ? (e, event) -> handleStatusClick(editor, event) : null;
|
||||
highlighter.setLineMarkerRenderer(LineStatusMarkerRenderer.createRenderer(line1, line2, color, tooltip, clickHandler));
|
||||
|
||||
myHighlighters.add(highlighter);
|
||||
public int getIndex() {
|
||||
return myIndex;
|
||||
}
|
||||
|
||||
private void handleStatusClick(@NotNull EditorEx editor, @NotNull MouseEvent event) {
|
||||
assert myAppliedTo != null;
|
||||
EditorEx resultEditor = myViewer.getResultEditor();
|
||||
EditorEx patchEditor = myViewer.getPatchEditor();
|
||||
@NotNull
|
||||
public HunkStatus getStatus() {
|
||||
return myStatus;
|
||||
}
|
||||
|
||||
Side clickSide = Side.fromLeft(editor == resultEditor);
|
||||
int clickLine = clickSide.select(myAppliedTo.start, myPatchInsertionRange.start);
|
||||
EditorEx clickEditor = clickSide.select(resultEditor, patchEditor);
|
||||
int targetY = clickEditor.logicalPositionToXY(new LogicalPosition(clickLine, 0)).y;
|
||||
int topShift = targetY - clickEditor.getScrollingModel().getVerticalScrollOffset();
|
||||
@NotNull
|
||||
public LineRange getPatchRange() {
|
||||
return new LineRange(myPatchDeletionRange.start, myPatchInsertionRange.end);
|
||||
}
|
||||
|
||||
int[] offsets = SyncScrollSupport.getTargetOffsets(resultEditor, patchEditor,
|
||||
myAppliedTo.start, myAppliedTo.end,
|
||||
myPatchInsertionRange.start, myPatchInsertionRange.end,
|
||||
topShift);
|
||||
DiffUtil.scrollToPoint(resultEditor, new Point(0, offsets[0]), false);
|
||||
DiffUtil.scrollToPoint(patchEditor, new Point(0, offsets[1]), false);
|
||||
@NotNull
|
||||
public LineRange getPatchAffectedRange() {
|
||||
return isRangeApplied() ? myPatchInsertionRange : myPatchDeletionRange;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LineRange getPatchDeletionRange() {
|
||||
return myPatchDeletionRange;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LineRange getPatchInsertionRange() {
|
||||
return myPatchInsertionRange;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LineRange getResultRange() {
|
||||
ApplyPatchViewer.MyModel model = myViewer.getModel();
|
||||
int lineStart = model.getLineStart(myIndex);
|
||||
int lineEnd = model.getLineEnd(myIndex);
|
||||
|
||||
if (lineStart != -1 || lineEnd != -1) return new LineRange(lineStart, lineEnd);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return myResolved;
|
||||
}
|
||||
|
||||
public void setResolved(boolean resolved) {
|
||||
myResolved = resolved;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextDiffType getDiffType() {
|
||||
return DiffUtil.getDiffType(!myPatchDeletionRange.isEmpty(), !myPatchInsertionRange.isEmpty());
|
||||
}
|
||||
|
||||
public boolean isRangeApplied() {
|
||||
return myResolved || getStatus() == HunkStatus.ALREADY_APPLIED;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -154,76 +257,127 @@ class ApplyPatchChange {
|
||||
}
|
||||
}
|
||||
|
||||
private void installPersistentHighlighters() {
|
||||
EditorEx patchEditor = myViewer.getPatchEditor();
|
||||
DocumentEx patchDocument = patchEditor.getDocument();
|
||||
//
|
||||
// Operations
|
||||
//
|
||||
|
||||
if (myPatchDeletionRange.isEmpty() || myPatchInsertionRange.isEmpty()) {
|
||||
DiffDrawUtil.createUnifiedChunkHighlighters(patchEditor, myPatchDeletionRange, myPatchInsertionRange, false);
|
||||
private void createOperations() {
|
||||
if (myViewer.isReadOnly()) return;
|
||||
if (isResolved()) return;
|
||||
|
||||
if (myStatus == HunkStatus.EXACTLY_APPLIED) {
|
||||
ContainerUtil.addIfNotNull(myOperations, createOperation(OperationType.APPLY));
|
||||
}
|
||||
else {
|
||||
DiffDrawUtil.createUnifiedChunkHighlighters(patchEditor, myPatchDeletionRange, myPatchInsertionRange, true);
|
||||
ContainerUtil.addIfNotNull(myOperations, createOperation(OperationType.IGNORE));
|
||||
}
|
||||
|
||||
CharSequence deleted = DiffUtil.getLinesContent(patchDocument, myPatchDeletionRange.start, myPatchDeletionRange.end);
|
||||
CharSequence inserted = DiffUtil.getLinesContent(patchDocument, myPatchInsertionRange.start, myPatchInsertionRange.end);
|
||||
List<DiffFragment> wordDiff = ByWord.compare(deleted, inserted, ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE);
|
||||
@Nullable
|
||||
private MyGutterOperation createOperation(@NotNull OperationType type) {
|
||||
if (isResolved()) return null;
|
||||
|
||||
int deletionStartShift = patchDocument.getLineStartOffset(myPatchDeletionRange.start);
|
||||
int insertionStartShift = patchDocument.getLineStartOffset(myPatchInsertionRange.start);
|
||||
for (DiffFragment fragment : wordDiff) {
|
||||
int deletedWordStart = deletionStartShift + fragment.getStartOffset1();
|
||||
int deletedWordEnd = deletionStartShift + fragment.getEndOffset1();
|
||||
DiffDrawUtil.createInlineHighlighter(patchEditor, deletedWordStart, deletedWordEnd, TextDiffType.DELETED);
|
||||
EditorEx editor = myViewer.getPatchEditor();
|
||||
Document document = editor.getDocument();
|
||||
|
||||
int insertedWordStart = insertionStartShift + fragment.getStartOffset2();
|
||||
int insertedWordEndEnd = insertionStartShift + fragment.getEndOffset2();
|
||||
DiffDrawUtil.createInlineHighlighter(patchEditor, insertedWordStart, insertedWordEndEnd, TextDiffType.INSERTED);
|
||||
int line = getPatchRange().start;
|
||||
int offset = line == DiffUtil.getLineCount(document) ? document.getTextLength() : document.getLineStartOffset(line);
|
||||
|
||||
RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(offset, offset,
|
||||
HighlighterLayer.ADDITIONAL_SYNTAX,
|
||||
null,
|
||||
HighlighterTargetArea.LINES_IN_RANGE);
|
||||
return new MyGutterOperation(highlighter, type);
|
||||
}
|
||||
|
||||
private class MyGutterOperation {
|
||||
@NotNull private final RangeHighlighter myHighlighter;
|
||||
@NotNull private final OperationType myType;
|
||||
|
||||
private MyGutterOperation(@NotNull RangeHighlighter highlighter, @NotNull OperationType type) {
|
||||
myHighlighter = highlighter;
|
||||
myType = type;
|
||||
|
||||
myHighlighter.setGutterIconRenderer(createRenderer());
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
myHighlighter.dispose();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GutterIconRenderer createRenderer() {
|
||||
switch (myType) {
|
||||
case APPLY:
|
||||
return createApplyRenderer();
|
||||
case IGNORE:
|
||||
return createIgnoreRenderer();
|
||||
default:
|
||||
throw new IllegalArgumentException(myType.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public LineRange getPatchDeletionRange() {
|
||||
return myPatchDeletionRange;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LineRange getPatchInsertionRange() {
|
||||
return myPatchInsertionRange;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AppliedTextPatch.HunkStatus getStatus() {
|
||||
return myStatus;
|
||||
@Nullable
|
||||
private GutterIconRenderer createApplyRenderer() {
|
||||
return createIconRenderer(DiffBundle.message("merge.dialog.apply.change.action.name"), DiffUtil.getArrowIcon(Side.RIGHT), () -> {
|
||||
myViewer.executeCommand("Accept change", () -> {
|
||||
myViewer.replaceChange(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LineRange getAppliedTo() {
|
||||
return myAppliedTo;
|
||||
private GutterIconRenderer createIgnoreRenderer() {
|
||||
return createIconRenderer(DiffBundle.message("merge.dialog.ignore.change.action.name"), AllIcons.Diff.Remove, () -> {
|
||||
myViewer.executeCommand("Ignore change", () -> {
|
||||
myViewer.markChangeResolved(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return myIsValid;
|
||||
@Nullable
|
||||
private static GutterIconRenderer createIconRenderer(@NotNull final String text,
|
||||
@NotNull final Icon icon,
|
||||
@NotNull final Runnable perform) {
|
||||
final String tooltipText = DiffUtil.createTooltipText(text, null);
|
||||
return new DiffGutterRenderer(icon, tooltipText) {
|
||||
@Override
|
||||
protected void performAction(AnActionEvent e) {
|
||||
perform.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private enum OperationType {
|
||||
APPLY, IGNORE
|
||||
}
|
||||
|
||||
//
|
||||
// Shift
|
||||
// State
|
||||
//
|
||||
|
||||
public boolean processChange(int oldLine1, int oldLine2, int shift) {
|
||||
if (myAppliedTo == null) return false;
|
||||
int line1 = myAppliedTo.start;
|
||||
int line2 = myAppliedTo.end;
|
||||
@NotNull
|
||||
public State storeState() {
|
||||
LineRange resultRange = getResultRange();
|
||||
return new State(
|
||||
myIndex,
|
||||
resultRange != null ? resultRange.start : -1,
|
||||
resultRange != null ? resultRange.end : -1,
|
||||
myResolved);
|
||||
}
|
||||
|
||||
DiffUtil.UpdatedLineRange newRange = DiffUtil.updateRangeOnModification(line1, line2, oldLine1, oldLine2, shift, true, true);
|
||||
public void restoreState(@NotNull State state) {
|
||||
myResolved = state.myResolved;
|
||||
}
|
||||
|
||||
myAppliedTo = new LineRange(newRange.startLine, newRange.endLine);
|
||||
if (newRange.damaged) myIsValid = false;
|
||||
public static class State extends MergeModelBase.State {
|
||||
private final boolean myResolved;
|
||||
|
||||
return newRange.damaged || myAppliedTo.start != line1 || myAppliedTo.end != line2;
|
||||
public State(int index,
|
||||
int startLine,
|
||||
int endLine,
|
||||
boolean resolved) {
|
||||
super(index, startLine, endLine);
|
||||
myResolved = resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-11
@@ -15,48 +15,52 @@
|
||||
*/
|
||||
package com.intellij.openapi.vcs.changes.patch.tool;
|
||||
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ApplyPatchDiffRequest extends DiffRequest {
|
||||
public class ApplyPatchDiffRequest extends DiffRequest implements ApplyPatchRequest {
|
||||
@NotNull private final DocumentContent myResultContent;
|
||||
@NotNull private final AppliedTextPatch myAppliedPatch;
|
||||
|
||||
@NotNull private final String myLocalContent;
|
||||
@Nullable private final VirtualFile myHighlightFile;
|
||||
|
||||
@Nullable private final String myWindowTitle;
|
||||
@NotNull private final String myLocalTitle;
|
||||
@NotNull private final String myResultTitle;
|
||||
@NotNull private final String myPatchTitle;
|
||||
|
||||
public ApplyPatchDiffRequest(@NotNull AppliedTextPatch appliedPatch,
|
||||
public ApplyPatchDiffRequest(@NotNull DocumentContent resultContent,
|
||||
@NotNull AppliedTextPatch appliedPatch,
|
||||
@NotNull String localContent,
|
||||
@Nullable VirtualFile highlightFile,
|
||||
@Nullable String windowTitle,
|
||||
@NotNull String localTitle,
|
||||
@NotNull String resultTitle,
|
||||
@NotNull String patchTitle) {
|
||||
myResultContent = resultContent;
|
||||
myAppliedPatch = appliedPatch;
|
||||
myLocalContent = localContent;
|
||||
myHighlightFile = highlightFile;
|
||||
myWindowTitle = windowTitle;
|
||||
myLocalTitle = localTitle;
|
||||
myResultTitle = resultTitle;
|
||||
myPatchTitle = patchTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentContent getResultContent() {
|
||||
return myResultContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLocalContent() {
|
||||
return myLocalContent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VirtualFile getHighlightFile() {
|
||||
return myHighlightFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public AppliedTextPatch getPatch() {
|
||||
return myAppliedPatch;
|
||||
@@ -68,16 +72,19 @@ public class ApplyPatchDiffRequest extends DiffRequest {
|
||||
return myWindowTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLocalTitle() {
|
||||
return myLocalTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getResultTitle() {
|
||||
return myResultTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getPatchTitle() {
|
||||
return myPatchTitle;
|
||||
|
||||
+8
-2
@@ -40,13 +40,19 @@ public class ApplyPatchDiffTool implements FrameDiffTool {
|
||||
|
||||
private static class MyApplyPatchViewer extends ApplyPatchViewer implements DiffViewer {
|
||||
public MyApplyPatchViewer(@NotNull DiffContext context, @NotNull ApplyPatchDiffRequest request) {
|
||||
super(context, request, null);
|
||||
super(context, request);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ToolbarComponents init() {
|
||||
return super.doInit();
|
||||
initPatchViewer();
|
||||
|
||||
ToolbarComponents components = new ToolbarComponents();
|
||||
components.statusPanel = getStatusPanel();
|
||||
components.toolbarActions = createToolbarActions();
|
||||
|
||||
return components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-11
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
package com.intellij.openapi.vcs.changes.patch.tool;
|
||||
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.diff.merge.MergeResult;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
|
||||
@@ -27,13 +27,13 @@ import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ApplyPatchMergeRequest extends MergeRequest {
|
||||
public class ApplyPatchMergeRequest extends MergeRequest implements ApplyPatchRequest {
|
||||
@Nullable private final Project myProject;
|
||||
|
||||
@NotNull private final Document myDocument;
|
||||
@NotNull private final CharSequence myOriginalContent;
|
||||
|
||||
@NotNull private final DocumentContent myResultContent;
|
||||
@NotNull private final AppliedTextPatch myAppliedPatch;
|
||||
|
||||
@NotNull private final CharSequence myOriginalContent;
|
||||
@NotNull private final String myLocalContent;
|
||||
|
||||
@Nullable private final String myWindowTitle;
|
||||
@@ -44,7 +44,7 @@ public class ApplyPatchMergeRequest extends MergeRequest {
|
||||
@Nullable private final Consumer<MergeResult> myCallback;
|
||||
|
||||
public ApplyPatchMergeRequest(@Nullable Project project,
|
||||
@NotNull Document document,
|
||||
@NotNull DocumentContent resultContent,
|
||||
@NotNull AppliedTextPatch appliedPatch,
|
||||
@NotNull String localContent,
|
||||
@Nullable String windowTitle,
|
||||
@@ -53,13 +53,13 @@ public class ApplyPatchMergeRequest extends MergeRequest {
|
||||
@NotNull String patchTitle,
|
||||
@Nullable Consumer<MergeResult> callback) {
|
||||
myProject = project;
|
||||
myDocument = document;
|
||||
myResultContent = resultContent;
|
||||
myAppliedPatch = appliedPatch;
|
||||
|
||||
myOriginalContent = ApplicationManager.getApplication().runReadAction(new Computable<CharSequence>() {
|
||||
@Override
|
||||
public CharSequence compute() {
|
||||
return myDocument.getImmutableCharSequence();
|
||||
return myResultContent.getDocument().getImmutableCharSequence();
|
||||
}
|
||||
});
|
||||
myLocalContent = localContent;
|
||||
@@ -77,16 +77,19 @@ public class ApplyPatchMergeRequest extends MergeRequest {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Document getDocument() {
|
||||
return myDocument;
|
||||
public DocumentContent getResultContent() {
|
||||
return myResultContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLocalContent() {
|
||||
return myLocalContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public AppliedTextPatch getPatch() {
|
||||
return myAppliedPatch;
|
||||
@@ -98,16 +101,19 @@ public class ApplyPatchMergeRequest extends MergeRequest {
|
||||
return myWindowTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLocalTitle() {
|
||||
return myLocalTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getResultTitle() {
|
||||
return myResultTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getPatchTitle() {
|
||||
return myPatchTitle;
|
||||
@@ -137,7 +143,7 @@ public class ApplyPatchMergeRequest extends MergeRequest {
|
||||
new WriteCommandAction.Simple(myProject) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
myDocument.setText(applyContent);
|
||||
myResultContent.getDocument().setText(applyContent);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
+36
-34
@@ -16,16 +16,20 @@
|
||||
package com.intellij.openapi.vcs.changes.patch.tool;
|
||||
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.diff.FrameDiffTool;
|
||||
import com.intellij.diff.merge.*;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.BooleanGetter;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.*;
|
||||
|
||||
import static com.intellij.diff.merge.MergeUtil.createSimpleResolveAction;
|
||||
|
||||
public class ApplyPatchMergeTool implements MergeTool {
|
||||
@NotNull
|
||||
@@ -44,7 +48,7 @@ public class ApplyPatchMergeTool implements MergeTool {
|
||||
@NotNull private final ApplyPatchMergeRequest myMergeRequest;
|
||||
|
||||
public MyApplyPatchViewer(@NotNull MergeContext context, @NotNull ApplyPatchMergeRequest request) {
|
||||
super(createWrapperDiffContext(context), createWrapperDiffRequest(request), request.getDocument());
|
||||
super(createWrapperDiffContext(context), request);
|
||||
myMergeContext = context;
|
||||
myMergeRequest = request;
|
||||
}
|
||||
@@ -54,28 +58,16 @@ public class ApplyPatchMergeTool implements MergeTool {
|
||||
return new MergeUtil.ProxyDiffContext(mergeContext);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ApplyPatchDiffRequest createWrapperDiffRequest(@NotNull ApplyPatchMergeRequest request) {
|
||||
VirtualFile file = FileDocumentManager.getInstance().getFile(request.getDocument());
|
||||
return new ApplyPatchDiffRequest(request.getPatch(), request.getLocalContent(), file, request.getTitle(),
|
||||
request.getLocalTitle(), request.getResultTitle(), request.getPatchTitle());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ToolbarComponents init() {
|
||||
initPatchViewer();
|
||||
|
||||
ToolbarComponents components = new ToolbarComponents();
|
||||
components.statusPanel = getStatusPanel();
|
||||
components.toolbarActions = createToolbarActions();
|
||||
|
||||
FrameDiffTool.ToolbarComponents init = super.doInit();
|
||||
components.statusPanel = init.statusPanel;
|
||||
components.toolbarActions = init.toolbarActions;
|
||||
|
||||
components.closeHandler = new BooleanGetter() {
|
||||
@Override
|
||||
public boolean get() {
|
||||
return MergeUtil.showExitWithoutApplyingChangesDialog(MyApplyPatchViewer.this, myMergeRequest, myMergeContext);
|
||||
}
|
||||
};
|
||||
components.closeHandler = () -> MergeUtil.showExitWithoutApplyingChangesDialog(this, myMergeRequest, myMergeContext);
|
||||
return components;
|
||||
}
|
||||
|
||||
@@ -83,18 +75,28 @@ public class ApplyPatchMergeTool implements MergeTool {
|
||||
@Override
|
||||
public Action getResolveAction(@NotNull final MergeResult result) {
|
||||
if (result == MergeResult.LEFT || result == MergeResult.RIGHT) return null;
|
||||
return createSimpleResolveAction(result, myMergeRequest, myMergeContext, this);
|
||||
}
|
||||
|
||||
String caption = MergeUtil.getResolveActionTitle(result, myMergeRequest, myMergeContext);
|
||||
return new AbstractAction(caption) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (result == MergeResult.CANCEL &&
|
||||
!MergeUtil.showExitWithoutApplyingChangesDialog(MyApplyPatchViewer.this, myMergeRequest, myMergeContext)) {
|
||||
return;
|
||||
}
|
||||
myMergeContext.finishMerge(result);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
protected void onChangeResolved() {
|
||||
super.onChangeResolved();
|
||||
|
||||
if (!ContainerUtil.exists(getModelChanges(), (c) -> !c.isResolved())) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (isDisposed()) return;
|
||||
|
||||
JComponent component = getComponent();
|
||||
int yOffset = new RelativePoint(getResultEditor().getComponent(), new Point(0, JBUI.scale(5))).getPoint(component).y;
|
||||
RelativePoint point = new RelativePoint(component, new Point(component.getWidth() / 2, yOffset));
|
||||
|
||||
String message = DiffBundle.message("apply.patch.all.changes.processed.message.text");
|
||||
DiffUtil.showSuccessPopup(message, point, this, () -> {
|
||||
if (isDisposed()) return;
|
||||
myMergeContext.finishMerge(MergeResult.RESOLVED);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.openapi.vcs.changes.patch.tool;
|
||||
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
interface ApplyPatchRequest {
|
||||
@NotNull
|
||||
DocumentContent getResultContent();
|
||||
|
||||
@NotNull
|
||||
String getLocalContent();
|
||||
|
||||
@NotNull
|
||||
AppliedTextPatch getPatch();
|
||||
|
||||
@Nullable
|
||||
String getTitle();
|
||||
|
||||
@NotNull
|
||||
String getLocalTitle();
|
||||
|
||||
@NotNull
|
||||
String getResultTitle();
|
||||
|
||||
@NotNull
|
||||
String getPatchTitle();
|
||||
}
|
||||
+559
-139
@@ -17,110 +17,200 @@ package com.intellij.openapi.vcs.changes.patch.tool;
|
||||
|
||||
import com.intellij.diff.DiffContentFactory;
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.diff.FrameDiffTool;
|
||||
import com.intellij.diff.DiffDialogHints;
|
||||
import com.intellij.diff.DiffManager;
|
||||
import com.intellij.diff.actions.ProxyUndoRedoAction;
|
||||
import com.intellij.diff.actions.impl.FocusOppositePaneAction;
|
||||
import com.intellij.diff.actions.impl.SetEditorSettingsAction;
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.diff.merge.MergeModelBase;
|
||||
import com.intellij.diff.requests.SimpleDiffRequest;
|
||||
import com.intellij.diff.tools.fragmented.LineNumberConvertor;
|
||||
import com.intellij.diff.tools.simple.SimpleDiffViewer;
|
||||
import com.intellij.diff.tools.util.DiffSplitter;
|
||||
import com.intellij.diff.tools.util.StatusPanel;
|
||||
import com.intellij.diff.tools.holders.TextEditorHolder;
|
||||
import com.intellij.diff.tools.util.*;
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder;
|
||||
import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
|
||||
import com.intellij.diff.tools.util.side.TwosideContentPanel;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.command.undo.DocumentReference;
|
||||
import com.intellij.openapi.command.undo.DocumentReferenceManager;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil;
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.event.VisibleAreaListener;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.EditorMarkupModel;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.BooleanGetter;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
class ApplyPatchViewer implements Disposable {
|
||||
@NotNull private final ApplyPatchDiffRequest myPatchRequest;
|
||||
class ApplyPatchViewer implements DataProvider, Disposable {
|
||||
public static final Logger LOG = Logger.getInstance(ApplyPatchViewer.class);
|
||||
|
||||
@Nullable private final Project myProject;
|
||||
@NotNull private final Document myResultDocument;
|
||||
@NotNull private final DiffContext myContext;
|
||||
@NotNull private final ApplyPatchRequest myPatchRequest;
|
||||
|
||||
@NotNull private final MySimpleDiffViewer myViewer;
|
||||
|
||||
@NotNull private final DiffSplitter myPatchSplitter;
|
||||
@NotNull private final EditorEx myPatchEditor;
|
||||
@NotNull private final TextEditorHolder myResultHolder;
|
||||
@NotNull private final TextEditorHolder myPatchHolder;
|
||||
@NotNull private final EditorEx myResultEditor;
|
||||
@NotNull private final EditorEx myPatchEditor;
|
||||
|
||||
@NotNull private final SimpleDiffPanel myPanel;
|
||||
@NotNull private final TwosideContentPanel myContentPanel;
|
||||
|
||||
@NotNull private final MyModel myModel;
|
||||
|
||||
@NotNull private final FocusTrackerSupport<Side> myFocusTrackerSupport;
|
||||
@NotNull private final MyPrevNextDifferenceIterable myPrevNextDifferenceIterable;
|
||||
@NotNull private final StatusPanel myStatusPanel;
|
||||
|
||||
@NotNull private final SetEditorSettingsAction myEditorSettingsAction;
|
||||
|
||||
// Changes with known AppliedTo. Ordered as in result-editor
|
||||
@NotNull private final List<ApplyPatchChange> myResultChanges = new ArrayList<>();
|
||||
// All changes. Ordered as in patch-editor
|
||||
@NotNull private final List<ApplyPatchChange> myPatchChanges = new ArrayList<>();
|
||||
// All changes. Ordered as in result-editor. Non-applied changes are at the very beginning with model ranges [-1. -1)
|
||||
@NotNull private final List<ApplyPatchChange> myModelChanges = new ArrayList<>();
|
||||
|
||||
public ApplyPatchViewer(@NotNull DiffContext context,
|
||||
@NotNull ApplyPatchDiffRequest patchRequest,
|
||||
@Nullable Document resultDocument) {
|
||||
myPatchRequest = patchRequest;
|
||||
private boolean myDisposed;
|
||||
|
||||
public ApplyPatchViewer(@NotNull DiffContext context, @NotNull ApplyPatchRequest request) {
|
||||
myProject = context.getProject();
|
||||
myResultDocument = resultDocument != null ? resultDocument : new DocumentImpl("");
|
||||
myContext = context;
|
||||
myPatchRequest = request;
|
||||
|
||||
VirtualFile file = patchRequest.getHighlightFile();
|
||||
DiffContentFactory contentFactory = DiffContentFactory.getInstance();
|
||||
DocumentContent localContent = contentFactory.create(myPatchRequest.getLocalContent(), file);
|
||||
DocumentContent mergedContent = contentFactory.create(myProject, myResultDocument, file);
|
||||
|
||||
SimpleDiffRequest diffRequest = new SimpleDiffRequest(myPatchRequest.getTitle(), localContent, mergedContent,
|
||||
myPatchRequest.getLocalTitle(), myPatchRequest.getResultTitle());
|
||||
if (resultDocument == null) diffRequest.putUserData(DiffUserDataKeys.FORCE_READ_ONLY, true);
|
||||
DocumentContent resultContent = request.getResultContent();
|
||||
DocumentContent patchContent = DiffContentFactory.getInstance().create(new DocumentImpl("", true), resultContent);
|
||||
|
||||
myViewer = new MySimpleDiffViewer(context, diffRequest);
|
||||
myResultEditor = myViewer.getEditor2();
|
||||
myResultHolder = TextEditorHolder.create(myProject, resultContent);
|
||||
myPatchHolder = TextEditorHolder.create(myProject, patchContent);
|
||||
|
||||
myPatchEditor = DiffUtil.createEditor(new DocumentImpl("", true), myProject, true, true);
|
||||
DiffUtil.setEditorHighlighter(myProject, myPatchEditor, mergedContent);
|
||||
myResultEditor = myResultHolder.getEditor();
|
||||
myPatchEditor = myPatchHolder.getEditor();
|
||||
|
||||
if (isReadOnly()) myResultEditor.setViewer(true);
|
||||
myPatchEditor.setViewer(true);
|
||||
|
||||
DiffUtil.disableBlitting(myResultEditor);
|
||||
DiffUtil.disableBlitting(myPatchEditor);
|
||||
|
||||
((EditorMarkupModel)myResultEditor.getMarkupModel()).setErrorStripeVisible(false);
|
||||
myResultEditor.setVerticalScrollbarOrientation(EditorEx.VERTICAL_SCROLLBAR_LEFT);
|
||||
|
||||
myPatchEditor.getGutterComponentEx().setForceShowRightFreePaintersArea(true);
|
||||
|
||||
JPanel patchPanel = new JPanel(new BorderLayout(0, DiffUtil.TITLE_GAP));
|
||||
patchPanel.add(myPatchEditor.getComponent(), BorderLayout.CENTER);
|
||||
patchPanel.add(myViewer.getPatchTitle(), BorderLayout.NORTH);
|
||||
((EditorMarkupModel)myPatchEditor.getMarkupModel()).setErrorStripeVisible(false);
|
||||
|
||||
|
||||
myPatchSplitter = new DiffSplitter();
|
||||
myPatchSplitter.setProportion(2f / 3);
|
||||
myPatchSplitter.setFirstComponent(myViewer.getComponent());
|
||||
myPatchSplitter.setSecondComponent(patchPanel);
|
||||
myPatchSplitter.setHonorComponentsMinimumSize(false);
|
||||
List<TextEditorHolder> holders = ContainerUtil.list(myResultHolder, myPatchHolder);
|
||||
List<EditorEx> editors = ContainerUtil.list(myResultEditor, myPatchEditor);
|
||||
JComponent resultTitle = DiffUtil.createTitle(myPatchRequest.getResultTitle());
|
||||
JComponent patchTitle = DiffUtil.createTitle(myPatchRequest.getPatchTitle());
|
||||
List<JComponent> titleComponents = DiffUtil.createSyncHeightComponents(ContainerUtil.list(resultTitle, patchTitle));
|
||||
|
||||
myContentPanel = new TwosideContentPanel(holders, titleComponents);
|
||||
myPanel = new SimpleDiffPanel(myContentPanel, this, myContext);
|
||||
|
||||
myModel = new MyModel(myProject, myResultEditor.getDocument());
|
||||
|
||||
myFocusTrackerSupport = new FocusTrackerSupport.Twoside(holders);
|
||||
myFocusTrackerSupport.setCurrentSide(Side.LEFT);
|
||||
myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
|
||||
myStatusPanel = new MyStatusPanel();
|
||||
|
||||
|
||||
new MyFocusOppositePaneAction().install(myPanel);
|
||||
new TextDiffViewerUtil.EditorActionsPopup(createEditorPopupActions()).install(editors);
|
||||
|
||||
new TextDiffViewerUtil.EditorFontSizeSynchronizer(editors).install(this);
|
||||
|
||||
TextDiffSettingsHolder.TextDiffSettings textSettings = TextDiffSettingsHolder.getInstance().getSettings("ApplyPatch");
|
||||
myEditorSettingsAction = new SetEditorSettingsAction(textSettings, editors);
|
||||
myEditorSettingsAction.applyDefaults();
|
||||
|
||||
if (!isReadOnly()) {
|
||||
DiffUtil.registerAction(new ApplySelectedChangesAction(true), myPanel);
|
||||
DiffUtil.registerAction(new IgnoreSelectedChangesAction(true), myPanel);
|
||||
}
|
||||
|
||||
ProxyUndoRedoAction.register(myProject, myResultEditor, myContentPanel);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JComponent getComponent() {
|
||||
return myPatchSplitter;
|
||||
protected List<AnAction> createToolbarActions() {
|
||||
List<AnAction> group = new ArrayList<>();
|
||||
|
||||
if (!isReadOnly()) {
|
||||
group.add(new ShowDiffWithLocalAction());
|
||||
group.add(new ApplyNonConflictsAction());
|
||||
group.add(myEditorSettingsAction);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myViewer.getPreferredFocusedComponent();
|
||||
@NotNull
|
||||
private List<AnAction> createEditorPopupActions() {
|
||||
List<AnAction> group = new ArrayList<>();
|
||||
|
||||
if (!isReadOnly()) {
|
||||
group.add(new ApplySelectedChangesAction(false));
|
||||
group.add(new IgnoreSelectedChangesAction(false));
|
||||
}
|
||||
|
||||
group.add(Separator.getInstance());
|
||||
group.addAll(TextDiffViewerUtil.createEditorPopupActions());
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
EditorFactory.getInstance().releaseEditor(myPatchEditor);
|
||||
Disposer.dispose(myViewer);
|
||||
if (myDisposed) return;
|
||||
myDisposed = true;
|
||||
|
||||
Disposer.dispose(myResultHolder);
|
||||
Disposer.dispose(myPatchHolder);
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return !DiffUtil.canMakeWritable(myResultEditor.getDocument());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MyModel getModel() {
|
||||
return myModel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ApplyPatchChange> getModelChanges() {
|
||||
return myModelChanges;
|
||||
}
|
||||
|
||||
public boolean isDisposed() {
|
||||
return myDisposed;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -129,8 +219,13 @@ class ApplyPatchViewer implements Disposable {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MySimpleDiffViewer getViewer() {
|
||||
return myViewer;
|
||||
public JComponent getComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myResultEditor.getContentComponent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -144,31 +239,44 @@ class ApplyPatchViewer implements Disposable {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FrameDiffTool.ToolbarComponents doInit() {
|
||||
Document patchDocument = myPatchEditor.getDocument();
|
||||
public Side getCurrentSide() {
|
||||
return myFocusTrackerSupport.getCurrentSide();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getData(@NonNls String dataId) {
|
||||
if (CommonDataKeys.PROJECT.is(dataId)) {
|
||||
return myProject;
|
||||
}
|
||||
else if (DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.is(dataId)) {
|
||||
return myPrevNextDifferenceIterable;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// Impl
|
||||
//
|
||||
|
||||
protected void initPatchViewer() {
|
||||
final Document outputDocument = myResultEditor.getDocument();
|
||||
DiffUtil.executeWriteCommand(outputDocument, myProject, "Init merge content", () -> {
|
||||
outputDocument.setText(myPatchRequest.getLocalContent());
|
||||
if (!isReadOnly()) DiffUtil.putNonundoableOperation(myProject, outputDocument);
|
||||
});
|
||||
|
||||
|
||||
PatchChangeBuilder builder = new PatchChangeBuilder();
|
||||
builder.exec(myPatchRequest.getPatch().getHunks(), myPatchRequest.getLocalContent());
|
||||
builder.exec(myPatchRequest.getPatch().getHunks());
|
||||
|
||||
|
||||
DiffUtil.executeWriteCommand(myResultDocument, myProject, "Init merge content", () -> {
|
||||
myResultDocument.setText(builder.getPatchApplyResult());
|
||||
|
||||
UndoManager undoManager = myProject != null ? UndoManager.getInstance(myProject) : UndoManager.getGlobalInstance();
|
||||
if (undoManager != null) {
|
||||
DocumentReference ref = DocumentReferenceManager.getInstance().create(myResultDocument);
|
||||
undoManager.nonundoableActionPerformed(ref, false);
|
||||
}
|
||||
});
|
||||
|
||||
Document patchDocument = myPatchEditor.getDocument();
|
||||
patchDocument.setText(builder.getPatchContent());
|
||||
|
||||
|
||||
LineNumberConvertor convertor = builder.getLineConvertor();
|
||||
myPatchEditor.getGutterComponentEx().setLineNumberConvertor(convertor.createConvertor1(), convertor.createConvertor2());
|
||||
|
||||
|
||||
TIntArrayList lines = builder.getSeparatorLines();
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
int offset = patchDocument.getLineStartOffset(lines.get(i));
|
||||
@@ -176,52 +284,388 @@ class ApplyPatchViewer implements Disposable {
|
||||
}
|
||||
|
||||
|
||||
for (PatchChangeBuilder.Hunk hunk : builder.getHunks()) {
|
||||
myPatchChanges.add(new ApplyPatchChange(this, hunk));
|
||||
List<PatchChangeBuilder.Hunk> hunks = builder.getHunks();
|
||||
|
||||
int[] modelToPatchIndexes = DiffUtil.getSortedIndexes(hunks, (h1, h2) -> {
|
||||
LineRange lines1 = h1.getAppliedToLines();
|
||||
LineRange lines2 = h2.getAppliedToLines();
|
||||
if (lines1 == null && lines2 == null) return 0;
|
||||
if (lines1 == null) return -1;
|
||||
if (lines2 == null) return 1;
|
||||
return lines1.start - lines2.start;
|
||||
});
|
||||
int[] patchToModelIndexes = DiffUtil.invertIndexes(modelToPatchIndexes);
|
||||
|
||||
List<LineRange> modelRanges = new ArrayList<>();
|
||||
for (int modelIndex = 0; modelIndex < hunks.size(); modelIndex++) {
|
||||
int patchIndex = modelToPatchIndexes[modelIndex];
|
||||
PatchChangeBuilder.Hunk hunk = hunks.get(patchIndex);
|
||||
LineRange resultRange = hunk.getAppliedToLines();
|
||||
|
||||
ApplyPatchChange change = new ApplyPatchChange(hunk, modelIndex, this);
|
||||
|
||||
myModelChanges.add(change);
|
||||
if (resultRange != null) myResultChanges.add(change);
|
||||
|
||||
modelRanges.add(resultRange != null ? resultRange : new LineRange(-1, -1));
|
||||
}
|
||||
myModel.setChanges(modelRanges);
|
||||
|
||||
for (int index : patchToModelIndexes) {
|
||||
myPatchChanges.add(myModelChanges.get(index));
|
||||
}
|
||||
|
||||
|
||||
for (ApplyPatchChange change : myModelChanges) {
|
||||
change.reinstallHighlighters();
|
||||
}
|
||||
myStatusPanel.update();
|
||||
|
||||
|
||||
myPatchSplitter.setPainter(new MyDividerPainter());
|
||||
myContentPanel.setPainter(new MyDividerPainter());
|
||||
|
||||
VisibleAreaListener areaListener = (e) -> myPatchSplitter.repaint();
|
||||
VisibleAreaListener areaListener = (e) -> myContentPanel.repaint();
|
||||
myResultEditor.getScrollingModel().addVisibleAreaListener(areaListener);
|
||||
myPatchEditor.getScrollingModel().addVisibleAreaListener(areaListener);
|
||||
|
||||
myResultEditor.getDocument().addDocumentListener(new MyDocumentListener(), this);
|
||||
|
||||
|
||||
FrameDiffTool.ToolbarComponents init = myViewer.init();
|
||||
init.statusPanel = getStatusPanel();
|
||||
return init;
|
||||
myPatchEditor.getGutterComponentEx().revalidateMarkup();
|
||||
}
|
||||
|
||||
private class MyDocumentListener implements DocumentListener {
|
||||
private boolean myShouldRepaint;
|
||||
public void scrollToChange(@NotNull ApplyPatchChange change, @NotNull Side masterSide, boolean forceScroll) {
|
||||
if (change.getResultRange() == null) {
|
||||
DiffUtil.moveCaret(myPatchEditor, change.getPatchRange().start);
|
||||
myPatchEditor.getScrollingModel().scrollToCaret(forceScroll ? ScrollType.CENTER : ScrollType.MAKE_VISIBLE);
|
||||
}
|
||||
else {
|
||||
LineRange resultRange = change.getResultRange();
|
||||
LineRange patchRange = change.getPatchAffectedRange();
|
||||
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent e) {
|
||||
myShouldRepaint = false;
|
||||
if (myPatchChanges.isEmpty()) return;
|
||||
|
||||
LineRange lineRange = DiffUtil.getAffectedLineRange(e);
|
||||
int shift = DiffUtil.countLinesShift(e);
|
||||
|
||||
for (ApplyPatchChange change : myPatchChanges) {
|
||||
myShouldRepaint |= change.processChange(lineRange.start, lineRange.end, shift);
|
||||
int topShift = -1;
|
||||
if (!forceScroll) {
|
||||
int masterLine = masterSide.select(resultRange.start, patchRange.start);
|
||||
EditorEx masterEditor = masterSide.select(myResultEditor, myPatchEditor);
|
||||
int targetY = masterEditor.logicalPositionToXY(new LogicalPosition(masterLine, 0)).y;
|
||||
int scrollOffset = masterEditor.getScrollingModel().getVerticalScrollOffset();
|
||||
topShift = targetY - scrollOffset;
|
||||
}
|
||||
|
||||
int[] offsets = SyncScrollSupport.getTargetOffsets(myResultEditor, myPatchEditor,
|
||||
resultRange.start, resultRange.end,
|
||||
patchRange.start, patchRange.end,
|
||||
topShift);
|
||||
|
||||
DiffUtil.moveCaret(myResultEditor, resultRange.start);
|
||||
DiffUtil.moveCaret(myPatchEditor, patchRange.start);
|
||||
|
||||
DiffUtil.scrollToPoint(myResultEditor, new Point(0, offsets[0]), false);
|
||||
DiffUtil.scrollToPoint(myPatchEditor, new Point(0, offsets[1]), false);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Modification operations
|
||||
//
|
||||
|
||||
public void repaintDivider() {
|
||||
myContentPanel.repaintDivider();
|
||||
}
|
||||
|
||||
public void executeCommand(@Nullable String commandName,
|
||||
@NotNull final Runnable task) {
|
||||
myModel.executeMergeCommand(commandName, null, UndoConfirmationPolicy.DEFAULT, false, null, task);
|
||||
}
|
||||
|
||||
class MyModel extends MergeModelBase<ApplyPatchChange.State> {
|
||||
public MyModel(@Nullable Project project, @NotNull Document document) {
|
||||
super(project, document);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent e) {
|
||||
if (myShouldRepaint) {
|
||||
myShouldRepaint = false;
|
||||
protected void reinstallHighlighters(int index) {
|
||||
ApplyPatchChange change = myModelChanges.get(index);
|
||||
change.reinstallHighlighters();
|
||||
}
|
||||
|
||||
myPatchSplitter.repaintDivider();
|
||||
for (ApplyPatchChange change : myPatchChanges) {
|
||||
change.reinstallHighlighters();
|
||||
@NotNull
|
||||
@Override
|
||||
protected ApplyPatchChange.State storeChangeState(int index) {
|
||||
ApplyPatchChange change = myModelChanges.get(index);
|
||||
return change.storeState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void restoreChangeState(@NotNull ApplyPatchChange.State state) {
|
||||
super.restoreChangeState(state);
|
||||
ApplyPatchChange change = myModelChanges.get(state.myIndex);
|
||||
|
||||
boolean wasResolved = change.isResolved();
|
||||
change.restoreState(state);
|
||||
if (wasResolved != change.isResolved()) onChangeResolved();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onChangeResolved() {
|
||||
if (isDisposed()) return;
|
||||
myStatusPanel.update();
|
||||
}
|
||||
|
||||
public void markChangeResolved(@NotNull ApplyPatchChange change) {
|
||||
if (change.isResolved()) return;
|
||||
|
||||
change.setResolved(true);
|
||||
myModel.invalidateHighlighters(change.getIndex());
|
||||
onChangeResolved();
|
||||
}
|
||||
|
||||
public void replaceChange(@NotNull ApplyPatchChange change) {
|
||||
LineRange resultRange = change.getResultRange();
|
||||
LineRange patchRange = change.getPatchInsertionRange();
|
||||
if (resultRange == null || change.isResolved()) return;
|
||||
if (change.getStatus() != AppliedTextPatch.HunkStatus.EXACTLY_APPLIED) return;
|
||||
|
||||
List<String> newContent = DiffUtil.getLines(myPatchEditor.getDocument(), patchRange.start, patchRange.end);
|
||||
myModel.replaceChange(change.getIndex(), newContent);
|
||||
|
||||
markChangeResolved(change);
|
||||
}
|
||||
|
||||
private class ApplySelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
private ApplySelectedChangesAction(boolean shortcut) {
|
||||
super(shortcut);
|
||||
getTemplatePresentation().setText("Accept");
|
||||
getTemplatePresentation().setIcon(AllIcons.Actions.Checked);
|
||||
copyShortcutFrom(ActionManager.getInstance().getAction("Diff.ApplyRightSide"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled(@NotNull ApplyPatchChange change) {
|
||||
return !change.isResolved() && change.getStatus() == AppliedTextPatch.HunkStatus.EXACTLY_APPLIED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<ApplyPatchChange> changes) {
|
||||
for (int i = changes.size() - 1; i >= 0; i--) {
|
||||
replaceChange(changes.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IgnoreSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
private IgnoreSelectedChangesAction(boolean shortcut) {
|
||||
super(shortcut);
|
||||
getTemplatePresentation().setText("Ignore");
|
||||
getTemplatePresentation().setIcon(AllIcons.Diff.Remove);
|
||||
copyShortcutFrom(ActionManager.getInstance().getAction("Diff.IgnoreRightSide"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled(@NotNull ApplyPatchChange change) {
|
||||
return !change.isResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<ApplyPatchChange> changes) {
|
||||
for (ApplyPatchChange change : changes) {
|
||||
markChangeResolved(change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ApplySelectedChangesActionBase extends DumbAwareAction {
|
||||
private final boolean myShortcut;
|
||||
|
||||
public ApplySelectedChangesActionBase(boolean shortcut) {
|
||||
myShortcut = shortcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
if (myShortcut) {
|
||||
// consume shortcut even if there are nothing to do - avoid calling some other action
|
||||
e.getPresentation().setEnabledAndVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Presentation presentation = e.getPresentation();
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
|
||||
Side side = Side.fromValue(ContainerUtil.list(myResultEditor, myPatchEditor), editor);
|
||||
if (side == null) {
|
||||
presentation.setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
presentation.setVisible(true);
|
||||
presentation.setEnabled(isSomeChangeSelected(side));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
final Side side = Side.fromValue(ContainerUtil.list(myResultEditor, myPatchEditor), editor);
|
||||
if (editor == null || side == null) return;
|
||||
|
||||
final List<ApplyPatchChange> selectedChanges = getSelectedChanges(side);
|
||||
if (selectedChanges.isEmpty()) return;
|
||||
|
||||
String title = e.getPresentation().getText() + " in patch resolve";
|
||||
|
||||
executeCommand(title, () -> {
|
||||
apply(selectedChanges);
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isSomeChangeSelected(@NotNull Side side) {
|
||||
EditorEx editor = side.select(myResultEditor, myPatchEditor);
|
||||
List<Caret> carets = editor.getCaretModel().getAllCarets();
|
||||
if (carets.size() != 1) return true;
|
||||
Caret caret = carets.get(0);
|
||||
if (caret.hasSelection()) return true;
|
||||
|
||||
int line = editor.getDocument().getLineNumber(editor.getExpectedCaretOffset());
|
||||
|
||||
List<ApplyPatchChange> changes = myModelChanges;
|
||||
for (ApplyPatchChange change : changes) {
|
||||
if (!isEnabled(change)) continue;
|
||||
LineRange range = side.select(change.getResultRange(), change.getPatchRange());
|
||||
if (range == null) continue;
|
||||
|
||||
if (DiffUtil.isSelectedByLine(line, range.start, range.end)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@CalledInAwt
|
||||
private List<ApplyPatchChange> getSelectedChanges(@NotNull Side side) {
|
||||
final BitSet lines = DiffUtil.getSelectedLines(side.select(myResultEditor, myPatchEditor));
|
||||
|
||||
List<ApplyPatchChange> affectedChanges = new ArrayList<>();
|
||||
for (ApplyPatchChange change : myModelChanges) {
|
||||
if (!isEnabled(change)) continue;
|
||||
LineRange range = side.select(change.getResultRange(), change.getPatchRange());
|
||||
if (range == null) continue;
|
||||
|
||||
if (DiffUtil.isSelectedByLine(lines, range.start, range.end)) {
|
||||
affectedChanges.add(change);
|
||||
}
|
||||
}
|
||||
return affectedChanges;
|
||||
}
|
||||
|
||||
protected abstract boolean isEnabled(@NotNull ApplyPatchChange change);
|
||||
|
||||
@CalledWithWriteLock
|
||||
protected abstract void apply(@NotNull List<ApplyPatchChange> changes);
|
||||
}
|
||||
|
||||
private class ApplyNonConflictsAction extends DumbAwareAction {
|
||||
public ApplyNonConflictsAction() {
|
||||
ActionUtil.copyFrom(this, "Diff.ApplyNonConflicts");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
boolean enabled = ContainerUtil.exists(myModelChanges, c -> {
|
||||
if (c.isResolved()) return false;
|
||||
if (c.getStatus() == AppliedTextPatch.HunkStatus.NOT_APPLIED) return false;
|
||||
return true;
|
||||
});
|
||||
e.getPresentation().setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
List<ApplyPatchChange> changes = myModelChanges;
|
||||
if (changes.isEmpty()) return;
|
||||
|
||||
executeCommand("Apply Non Conflicted Changes", () -> {
|
||||
for (int i = changes.size() - 1; i >= 0; i--) {
|
||||
ApplyPatchChange change = changes.get(i);
|
||||
switch (change.getStatus()) {
|
||||
case ALREADY_APPLIED:
|
||||
markChangeResolved(change);
|
||||
break;
|
||||
case EXACTLY_APPLIED:
|
||||
replaceChange(change);
|
||||
break;
|
||||
case NOT_APPLIED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Actions
|
||||
//
|
||||
|
||||
private class MyFocusOppositePaneAction extends FocusOppositePaneAction {
|
||||
public MyFocusOppositePaneAction() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
EditorEx targetEditor = getCurrentSide().other().select(myResultEditor, myPatchEditor);
|
||||
DiffUtil.requestFocus(myProject, targetEditor.getContentComponent());
|
||||
}
|
||||
}
|
||||
|
||||
private class ShowDiffWithLocalAction extends DumbAwareAction {
|
||||
public ShowDiffWithLocalAction() {
|
||||
super("Compare with local content", null, AllIcons.Diff.Diff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
DocumentContent resultContent = myPatchRequest.getResultContent();
|
||||
DocumentContent localContent = DiffContentFactory.getInstance().create(myPatchRequest.getLocalContent(), resultContent);
|
||||
|
||||
SimpleDiffRequest request = new SimpleDiffRequest(myPatchRequest.getTitle(),
|
||||
localContent, resultContent,
|
||||
myPatchRequest.getLocalTitle(), myPatchRequest.getResultTitle());
|
||||
|
||||
LogicalPosition currentPosition = DiffUtil.getCaretPosition(myResultEditor);
|
||||
request.putUserData(DiffUserDataKeys.SCROLL_TO_LINE, Pair.create(Side.RIGHT, currentPosition.line));
|
||||
|
||||
DiffManager.getInstance().showDiff(myProject, request, new DiffDialogHints(null, myPanel));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
private class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase<ApplyPatchChange> {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<ApplyPatchChange> getChanges() {
|
||||
return getCurrentSide().select(myResultChanges, myPatchChanges);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected EditorEx getEditor() {
|
||||
return getCurrentSide().select(myResultEditor, myPatchEditor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStartLine(@NotNull ApplyPatchChange change) {
|
||||
//noinspection ConstantConditions
|
||||
return getCurrentSide().select(change.getResultRange(), change.getPatchAffectedRange()).start;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getEndLine(@NotNull ApplyPatchChange change) {
|
||||
//noinspection ConstantConditions
|
||||
return getCurrentSide().select(change.getResultRange(), change.getPatchAffectedRange()).end;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scrollToChange(@NotNull ApplyPatchChange change) {
|
||||
ApplyPatchViewer.this.scrollToChange(change, getCurrentSide(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,17 +684,15 @@ class ApplyPatchViewer implements Disposable {
|
||||
|
||||
@Override
|
||||
public void process(@NotNull Handler handler) {
|
||||
for (ApplyPatchChange change : myPatchChanges) {
|
||||
LineRange appliedTo = change.getAppliedTo();
|
||||
if (appliedTo == null) continue;
|
||||
for (ApplyPatchChange change : myResultChanges) {
|
||||
LineRange resultRange = change.getResultRange();
|
||||
LineRange patchRange = change.getPatchRange();
|
||||
assert resultRange != null;
|
||||
|
||||
int patchLine1 = change.getPatchDeletionRange().start;
|
||||
int patchLine2 = change.getPatchInsertionRange().end;
|
||||
Color color = change.getDiffType().getColor(myPatchEditor);
|
||||
|
||||
Color color = TextDiffType.MODIFIED.getColor(myPatchEditor);
|
||||
|
||||
// do not abort - ranges are ordered in patch order, but they can be not ordered in terms of appliedTo
|
||||
handler.process(appliedTo.start, appliedTo.end, patchLine1, patchLine2, color);
|
||||
// do not abort - ranges are ordered in patch order, but they can be not ordered in terms of resultRange
|
||||
handler.process(resultRange.start, resultRange.end, patchRange.start, patchRange.end, color, change.isResolved());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,10 +701,13 @@ class ApplyPatchViewer implements Disposable {
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getMessage() {
|
||||
int total = myPatchChanges.size();
|
||||
int totalUnresolved = 0;
|
||||
int alreadyApplied = 0;
|
||||
int notApplied = 0;
|
||||
for (ApplyPatchChange change : myPatchChanges) {
|
||||
if (change.isResolved()) continue;
|
||||
|
||||
totalUnresolved++;
|
||||
switch (change.getStatus()) {
|
||||
case ALREADY_APPLIED:
|
||||
alreadyApplied++;
|
||||
@@ -275,42 +720,17 @@ class ApplyPatchViewer implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
if (total == notApplied) {
|
||||
if (totalUnresolved == 0) {
|
||||
return DiffBundle.message("apply.somehow.status.message.all.applied", notApplied);
|
||||
}
|
||||
if (totalUnresolved == notApplied) {
|
||||
return DiffBundle.message("apply.somehow.status.message.cant.apply", notApplied);
|
||||
}
|
||||
else {
|
||||
String message = DiffBundle.message("apply.somehow.status.message.cant.apply.some", notApplied, total);
|
||||
String message = DiffBundle.message("apply.somehow.status.message.cant.apply.some", notApplied, totalUnresolved);
|
||||
if (alreadyApplied == 0) return message;
|
||||
return message + ". " + DiffBundle.message("apply.somehow.status.message.already.applied", alreadyApplied);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MySimpleDiffViewer extends SimpleDiffViewer {
|
||||
private JComponent myPatchTitleComponent;
|
||||
|
||||
public MySimpleDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
|
||||
super(context, request);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<JComponent> createTitles() {
|
||||
List<JComponent> requestTitles = DiffUtil.createTextTitles(myRequest, getEditors());
|
||||
JComponent patchTitleLabel = DiffUtil.createTitle(myPatchRequest.getPatchTitle(), null, null, false);
|
||||
assert requestTitles.size() == 2;
|
||||
|
||||
List<JComponent> titles = ContainerUtil.append(requestTitles, patchTitleLabel);
|
||||
List<JComponent> syncTitles = DiffUtil.createSyncHeightComponents(titles);
|
||||
|
||||
JComponent title1 = syncTitles.get(0);
|
||||
JComponent title2 = syncTitles.get(1);
|
||||
myPatchTitleComponent = syncTitles.get(2);
|
||||
return ContainerUtil.list(title1, title2);
|
||||
}
|
||||
|
||||
public JComponent getPatchTitle() {
|
||||
return myPatchTitleComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-48
@@ -21,12 +21,14 @@ import com.intellij.diff.util.LineRange;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch.AppliedSplitPatchHunk;
|
||||
import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch.HunkStatus;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
class PatchChangeBuilder {
|
||||
@@ -34,18 +36,30 @@ class PatchChangeBuilder {
|
||||
@NotNull private final List<Hunk> myHunks = new ArrayList<>();
|
||||
@NotNull private final LineNumberConvertor.Builder myConvertor = new LineNumberConvertor.Builder();
|
||||
@NotNull private final TIntArrayList myChangedLines = new TIntArrayList();
|
||||
private CharSequence myPatchedContent;
|
||||
|
||||
private int totalLines = 0;
|
||||
|
||||
@NotNull
|
||||
public static CharSequence getPatchedContent(@NotNull AppliedTextPatch patch, @NotNull String localContent) {
|
||||
PatchChangeBuilder builder = new PatchChangeBuilder();
|
||||
builder.exec(patch.getHunks(), localContent);
|
||||
return builder.getPatchApplyResult();
|
||||
builder.exec(patch.getHunks());
|
||||
|
||||
DocumentImpl document = new DocumentImpl(localContent, true);
|
||||
List<Hunk> appliedHunks = ContainerUtil.filter(builder.getHunks(), (h) -> h.getStatus() == HunkStatus.EXACTLY_APPLIED);
|
||||
ContainerUtil.sort(appliedHunks, Comparator.comparingInt(h -> h.getAppliedToLines().start));
|
||||
|
||||
for (int i = appliedHunks.size() - 1; i >= 0; i--) {
|
||||
Hunk hunk = appliedHunks.get(i);
|
||||
LineRange appliedTo = hunk.getAppliedToLines();
|
||||
List<String> inserted = hunk.getInsertedLines();
|
||||
|
||||
DiffUtil.applyModification(document, appliedTo.start, appliedTo.end, inserted);
|
||||
}
|
||||
|
||||
return document.getText();
|
||||
}
|
||||
|
||||
public void exec(@NotNull List<AppliedSplitPatchHunk> splitHunks, @NotNull CharSequence localContent) {
|
||||
public void exec(@NotNull List<AppliedSplitPatchHunk> splitHunks) {
|
||||
int lastBeforeLine = -1;
|
||||
for (AppliedSplitPatchHunk hunk : splitHunks) {
|
||||
List<String> contextBefore = hunk.getContextBefore();
|
||||
@@ -87,30 +101,6 @@ class PatchChangeBuilder {
|
||||
|
||||
myHunks.add(new Hunk(hunk.getInsertedLines(), deletionRange, insertionRange, hunk.getAppliedTo(), hunk.getStatus()));
|
||||
}
|
||||
|
||||
|
||||
DocumentImpl document = new DocumentImpl(localContent, true);
|
||||
List<Hunk> appliedHunks = ContainerUtil.filter(myHunks, (h) -> h.getOriginalAppliedToLines() != null);
|
||||
ContainerUtil.sort(appliedHunks, (h1, h2) -> Integer.compare(h1.getOriginalAppliedToLines().start,
|
||||
h2.getOriginalAppliedToLines().start));
|
||||
|
||||
int shift = 0;
|
||||
for (Hunk hunk : appliedHunks) {
|
||||
LineRange appliedTo = hunk.getOriginalAppliedToLines();
|
||||
List<String> inserted = hunk.getInsertedLines();
|
||||
|
||||
int insertedLines = inserted.size();
|
||||
int deletedLines = appliedTo.end - appliedTo.start;
|
||||
|
||||
hunk.setAppliedToLines(new LineRange(appliedTo.start + shift, appliedTo.start + shift + insertedLines));
|
||||
|
||||
if (hunk.getStatus() == AppliedTextPatch.HunkStatus.EXACTLY_APPLIED) {
|
||||
DiffUtil.applyModification(document, appliedTo.start + shift, appliedTo.end + shift, inserted);
|
||||
shift += insertedLines - deletedLines;
|
||||
}
|
||||
}
|
||||
|
||||
myPatchedContent = document.getText();
|
||||
}
|
||||
|
||||
private void addContext(@NotNull List<String> context, int beforeLineNumber, int afterLineNumber) {
|
||||
@@ -156,11 +146,6 @@ class PatchChangeBuilder {
|
||||
return myChangedLines;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CharSequence getPatchApplyResult() {
|
||||
return myPatchedContent;
|
||||
}
|
||||
|
||||
|
||||
static class Hunk {
|
||||
@NotNull private final List<String> myInsertedLines;
|
||||
@@ -168,15 +153,13 @@ class PatchChangeBuilder {
|
||||
@NotNull private final LineRange myPatchInsertionRange;
|
||||
|
||||
@Nullable private final LineRange myAppliedToLines;
|
||||
@NotNull private final AppliedTextPatch.HunkStatus myStatus;
|
||||
|
||||
@Nullable private LineRange myUpdatedAppliedToLines;
|
||||
@NotNull private final HunkStatus myStatus;
|
||||
|
||||
public Hunk(@NotNull List<String> insertedLines,
|
||||
@NotNull LineRange patchDeletionRange,
|
||||
@NotNull LineRange patchInsertionRange,
|
||||
@Nullable LineRange appliedToLines,
|
||||
@NotNull AppliedTextPatch.HunkStatus status) {
|
||||
@NotNull HunkStatus status) {
|
||||
myInsertedLines = insertedLines;
|
||||
myPatchDeletionRange = patchDeletionRange;
|
||||
myPatchInsertionRange = patchInsertionRange;
|
||||
@@ -195,26 +178,17 @@ class PatchChangeBuilder {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AppliedTextPatch.HunkStatus getStatus() {
|
||||
public HunkStatus getStatus() {
|
||||
return myStatus;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LineRange getAppliedToLines() {
|
||||
return myUpdatedAppliedToLines;
|
||||
}
|
||||
|
||||
private void setAppliedToLines(@Nullable LineRange value) {
|
||||
myUpdatedAppliedToLines = value;
|
||||
return myAppliedToLines;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<String> getInsertedLines() {
|
||||
return myInsertedLines;
|
||||
}
|
||||
|
||||
private LineRange getOriginalAppliedToLines() {
|
||||
return myAppliedToLines;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user