diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml
index 399b350e3626..3a5fc95eaef1 100644
--- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml
+++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml
@@ -156,6 +156,7 @@
+
@@ -195,6 +196,7 @@
+
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryDiffPreview.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryDiffPreview.kt
new file mode 100644
index 000000000000..22f819f216f1
--- /dev/null
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryDiffPreview.kt
@@ -0,0 +1,51 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.vcs.log.history
+
+import com.intellij.diff.chains.DiffRequestProducer
+import com.intellij.diff.util.DiffPlaces
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vcs.changes.ChangeViewDiffRequestProcessor
+import com.intellij.ui.IdeBorderFactory
+import com.intellij.ui.SideBorder
+import com.intellij.util.containers.ContainerUtil
+import com.intellij.vcs.log.ui.frame.VcsLogChangesBrowser
+
+internal class FileHistoryDiffPreview(project: Project, private val changeGetter: () -> Change?,
+ disposable: Disposable) : ChangeViewDiffRequestProcessor(project, DiffPlaces.VCS_LOG_VIEW) {
+ init {
+ myContentPanel.border = IdeBorderFactory.createBorder(SideBorder.TOP)
+ Disposer.register(disposable, this)
+ }
+
+ override fun getSelectedChanges(): List = allChanges
+
+ override fun getAllChanges(): List {
+ val change = changeGetter() ?: return emptyList()
+ return listOf(MyChangeWrapper(change))
+ }
+
+ override fun selectChange(change: ChangeViewDiffRequestProcessor.Wrapper) {}
+
+ fun updatePreview(state: Boolean) {
+ if (state) {
+ refresh(false)
+ }
+ else {
+ clear()
+ }
+ }
+
+ private inner class MyChangeWrapper internal constructor(private val change: Change) : ChangeViewDiffRequestProcessor.Wrapper() {
+
+ override fun getUserObject(): Any {
+ return change
+ }
+
+ override fun createProducer(project: Project?): DiffRequestProducer? {
+ return VcsLogChangesBrowser.createDiffRequestProducer(project!!, change, ContainerUtil.newHashMap(), true)
+ }
+ }
+}
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java
index c28a18efdebf..fd861c0284a4 100644
--- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java
@@ -2,6 +2,7 @@
package com.intellij.vcs.log.history;
import com.google.common.util.concurrent.SettableFuture;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.vcs.FilePath;
@@ -9,6 +10,7 @@ import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.JBColor;
+import com.intellij.ui.OnePixelSplitter;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
@@ -33,11 +35,13 @@ import com.intellij.vcs.log.visible.VisiblePackRefresher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
+import javax.swing.event.ListSelectionListener;
import java.awt.*;
-import java.util.Collections;
import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
+
+import static com.intellij.util.ObjectUtils.notNull;
import static com.intellij.util.ObjectUtils.notNull;
@@ -51,6 +55,9 @@ public class FileHistoryUi extends AbstractVcsLogUi {
@NotNull private final FileHistoryUiProperties myUiProperties;
@NotNull private final FileHistoryFilterUi myFilterUi;
@NotNull private final FileHistoryPanel myFileHistoryPanel;
+ @Nullable private final FileHistoryDiffPreview myDiffPreview;
+ @Nullable private final OnePixelSplitter myDiffPreviewSplitter;
+ @NotNull private final JComponent myMainComponent;
@NotNull private final Set myHighlighterIds;
@NotNull private final MyPropertiesChangeListener myPropertiesChangeListener;
@@ -73,6 +80,27 @@ public class FileHistoryUi extends AbstractVcsLogUi {
myFilterUi = new FileHistoryFilterUi(path, revision, root, uiProperties);
myFileHistoryPanel = new FileHistoryPanel(this, logData, myVisiblePack, path);
+ if (!myPath.isDirectory()) {
+ myDiffPreview = new FileHistoryDiffPreview(myProject, () -> getSelectedChange(), this);
+ ListSelectionListener selectionListener = e -> {
+ int[] selection = getTable().getSelectedRows();
+ ApplicationManager.getApplication()
+ .invokeLater(() -> myDiffPreview.updatePreview(myUiProperties.get(CommonUiProperties.SHOW_DIFF_PREVIEW)),
+ o -> !Arrays.equals(selection, getTable().getSelectedRows()));
+ };
+ getTable().getSelectionModel().addListSelectionListener(selectionListener);
+
+ myDiffPreviewSplitter = new OnePixelSplitter(false, "vcs.history.diff.splitter.proportion", 0.7f);
+ myDiffPreviewSplitter.setFirstComponent(myFileHistoryPanel);
+ showDiffPreview(myUiProperties.get(CommonUiProperties.SHOW_DIFF_PREVIEW));
+ myMainComponent = myDiffPreviewSplitter;
+ }
+ else {
+ myDiffPreview = null;
+ myDiffPreviewSplitter = null;
+ myMainComponent = myFileHistoryPanel;
+ }
+
myHighlighterIds = myRevision == null
? ContainerUtil.newHashSet(MyCommitsHighlighter.Factory.ID,
CurrentBranchHighlighter.Factory.ID)
@@ -94,6 +122,10 @@ public class FileHistoryUi extends AbstractVcsLogUi {
return path.getPath() + (revision == null ? "" : revision.asString());
}
+ public boolean hasDiffPreview() {
+ return myDiffPreview != null;
+ }
+
@Nullable
public VcsFileRevision createRevision(@Nullable VcsCommitMetadata commit) {
if (commit == null) return null;
@@ -195,6 +227,13 @@ public class FileHistoryUi extends AbstractVcsLogUi {
return myPath.equals(targetPath) && Objects.equals(myRevision, targetRevision);
}
+ private void showDiffPreview(boolean state) {
+ if (myDiffPreview != null) {
+ myDiffPreview.updatePreview(state);
+ myDiffPreviewSplitter.setSecondComponent(state ? myDiffPreview.getComponent() : null);
+ }
+ }
+
@NotNull
@Override
public VcsLogFilterUi getFilterUi() {
@@ -209,6 +248,9 @@ public class FileHistoryUi extends AbstractVcsLogUi {
@Override
protected void onVisiblePackUpdated(boolean permGraphChanged) {
myFileHistoryPanel.updateDataPack(myVisiblePack, permGraphChanged);
+ if (myDiffPreview != null) {
+ myDiffPreview.updatePreview(myUiProperties.get(CommonUiProperties.SHOW_DIFF_PREVIEW));
+ }
}
@NotNull
@@ -220,7 +262,7 @@ public class FileHistoryUi extends AbstractVcsLogUi {
@NotNull
@Override
public Component getMainComponent() {
- return myFileHistoryPanel;
+ return myMainComponent;
}
@Nullable
@@ -260,6 +302,9 @@ public class FileHistoryUi extends AbstractVcsLogUi {
else if (property instanceof CommonUiProperties.TableColumnProperty) {
getTable().forceReLayout(((CommonUiProperties.TableColumnProperty)property).getColumn());
}
+ else if (CommonUiProperties.SHOW_DIFF_PREVIEW.equals(property)) {
+ showDiffPreview(myUiProperties.get(CommonUiProperties.SHOW_DIFF_PREVIEW));
+ }
}
}
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUiProperties.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUiProperties.java
index 0b228743bade..4ceb3fe4cedd 100644
--- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUiProperties.java
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUiProperties.java
@@ -8,8 +8,7 @@ import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
-import com.intellij.vcs.log.impl.CommonUiProperties;
-import com.intellij.vcs.log.impl.CommonUiProperties.TableColumnProperty;
+import com.intellij.vcs.log.impl.CommonUiProperties.*;
import com.intellij.vcs.log.impl.VcsLogUiProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -18,6 +17,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
+import static com.intellij.vcs.log.impl.CommonUiProperties.*;
import static com.intellij.vcs.log.ui.table.GraphTableModel.*;
@State(name = "Vcs.Log.History.Properties", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)})
@@ -31,19 +31,20 @@ public class FileHistoryUiProperties implements VcsLogUiProperties, PersistentSt
public boolean SHOW_OTHER_BRANCHES = false;
public Map COLUMN_WIDTH = ContainerUtil.newHashMap();
public List COLUMN_ORDER = ContainerUtil.newArrayList();
+ public boolean SHOW_DIFF_PREVIEW = true;
}
@SuppressWarnings("unchecked")
@NotNull
@Override
public T get(@NotNull VcsLogUiProperty property) {
- if (CommonUiProperties.SHOW_DETAILS.equals(property)) {
+ if (SHOW_DETAILS.equals(property)) {
return (T)Boolean.valueOf(myState.SHOW_DETAILS);
}
else if (SHOW_ALL_BRANCHES.equals(property)) {
return (T)Boolean.valueOf(myState.SHOW_OTHER_BRANCHES);
}
- else if (CommonUiProperties.COLUMN_ORDER.equals(property)) {
+ else if (COLUMN_ORDER.equals(property)) {
List order = myState.COLUMN_ORDER;
if (order == null || order.isEmpty()) {
order = ContainerUtilRt.newArrayList(ROOT_COLUMN, AUTHOR_COLUMN, DATE_COLUMN, COMMIT_COLUMN);
@@ -55,24 +56,30 @@ public class FileHistoryUiProperties implements VcsLogUiProperties, PersistentSt
if (savedWidth == null) return (T)Integer.valueOf(-1);
return (T)savedWidth;
}
+ else if (SHOW_DIFF_PREVIEW.equals(property)) {
+ return (T)Boolean.valueOf(myState.SHOW_DIFF_PREVIEW);
+ }
throw new UnsupportedOperationException("Unknown property " + property);
}
@SuppressWarnings("unchecked")
@Override
public void set(@NotNull VcsLogUiProperty property, @NotNull T value) {
- if (CommonUiProperties.SHOW_DETAILS.equals(property)) {
+ if (SHOW_DETAILS.equals(property)) {
myState.SHOW_DETAILS = (Boolean)value;
}
else if (SHOW_ALL_BRANCHES.equals(property)) {
myState.SHOW_OTHER_BRANCHES = (Boolean)value;
}
- else if (CommonUiProperties.COLUMN_ORDER.equals(property)) {
+ else if (COLUMN_ORDER.equals(property)) {
myState.COLUMN_ORDER = (List)value;
}
else if (property instanceof TableColumnProperty) {
myState.COLUMN_WIDTH.put(((TableColumnProperty)property).getColumn(), (Integer)value);
}
+ else if (SHOW_DIFF_PREVIEW.equals(property)) {
+ myState.SHOW_DIFF_PREVIEW = (Boolean)value;
+ }
else {
throw new UnsupportedOperationException("Unknown property " + property);
}
@@ -81,9 +88,10 @@ public class FileHistoryUiProperties implements VcsLogUiProperties, PersistentSt
@Override
public boolean exists(@NotNull VcsLogUiProperty property) {
- return CommonUiProperties.SHOW_DETAILS.equals(property) ||
+ return SHOW_DETAILS.equals(property) ||
SHOW_ALL_BRANCHES.equals(property) ||
- CommonUiProperties.COLUMN_ORDER.equals(property) ||
+ COLUMN_ORDER.equals(property) ||
+ SHOW_DIFF_PREVIEW.equals(property) ||
property instanceof TableColumnProperty;
}
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowDiffPreviewAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowDiffPreviewAction.java
index d2d062a27407..207478e7a6f1 100644
--- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowDiffPreviewAction.java
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowDiffPreviewAction.java
@@ -2,17 +2,32 @@
package com.intellij.vcs.log.ui.actions;
import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.vcs.log.history.FileHistoryUi;
import com.intellij.vcs.log.impl.CommonUiProperties;
import com.intellij.vcs.log.impl.VcsLogUiProperties;
+import com.intellij.vcs.log.ui.VcsLogInternalDataKeys;
+import org.jetbrains.annotations.NotNull;
public class ShowDiffPreviewAction extends BooleanPropertyToggleAction {
public ShowDiffPreviewAction() {
- super("Preview Diff", "Show Diff Preview Panel in Vcs Log", AllIcons.Actions.PreviewDetails);
+ super("Preview Diff", "Show Diff Preview Panel", AllIcons.Actions.PreviewDetails);
}
@Override
protected VcsLogUiProperties.VcsLogUiProperty getProperty() {
return CommonUiProperties.SHOW_DIFF_PREVIEW;
}
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ FileHistoryUi fileHistoryUi = e.getData(VcsLogInternalDataKeys.FILE_HISTORY_UI);
+ if (fileHistoryUi != null && !fileHistoryUi.hasDiffPreview()) {
+ e.getPresentation().setEnabledAndVisible(false);
+ }
+ else {
+ super.update(e);
+ }
+ }
}
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogChangesBrowser.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogChangesBrowser.java
index c25ec787910b..3eadd8d4650f 100644
--- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogChangesBrowser.java
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogChangesBrowser.java
@@ -54,7 +54,7 @@ import static com.intellij.vcs.log.impl.MainVcsLogUiProperties.SHOW_CHANGES_FROM
/**
* Change browser for commits in the Log. For merge commits, can display changes to commits parents in separate groups.
*/
-class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposable {
+public class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposable {
@NotNull private static final String EMPTY_SELECTION_TEXT = "Select commit to view details";
@NotNull private final Project myProject;
@NotNull private final MainVcsLogUiProperties myUiProperties;
@@ -69,9 +69,9 @@ class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposable {
@Nullable private Runnable myModelUpdateListener;
VcsLogChangesBrowser(@NotNull Project project,
- @NotNull MainVcsLogUiProperties uiProperties,
- @NotNull Function getter,
- @NotNull Disposable parent) {
+ @NotNull MainVcsLogUiProperties uiProperties,
+ @NotNull Function getter,
+ @NotNull Disposable parent) {
super(project, false, false);
myProject = project;
myUiProperties = uiProperties;
@@ -258,42 +258,54 @@ class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposable {
@Nullable
public ChangeDiffRequestChain.Producer getDiffRequestProducer(@NotNull Object userObject, boolean forDiffPreview) {
+ if (!(userObject instanceof Change)) return null;
+ Change change = (Change)userObject;
+
Map context = ContainerUtil.newHashMap();
- if (userObject instanceof MergedChange) {
- MergedChange mergedChange = (MergedChange)userObject;
- if (mergedChange.getSourceChanges().size() == 2) {
- if (forDiffPreview) {
- putFilePathsIntoContext(mergedChange, context);
- }
- return new MergedChangeDiffRequestProvider.MyProducer(myProject, mergedChange, context);
- }
+ if (!(change instanceof MergedChange)) {
+ putRootTagIntoChangeContext(change, context);
}
- if (userObject instanceof Change) {
- Change change = (Change)userObject;
-
- if (forDiffPreview) {
- putFilePathsIntoContext(change, context);
- }
-
- CommitId parentId = null;
- for (CommitId commitId : myChangesToParents.keySet()) {
- if (myChangesToParents.get(commitId).contains(change)) {
- parentId = commitId;
- break;
- }
- }
-
- if (parentId != null) {
- RootTag tag = new RootTag(parentId.getHash(), getText(parentId));
- context.put(ChangeDiffRequestProducer.TAG_KEY, tag);
- }
-
- return ChangeDiffRequestProducer.create(myProject, change, context);
- }
- return null;
+ return createDiffRequestProducer(myProject, change, context, forDiffPreview);
}
- private static void putFilePathsIntoContext(@NotNull MergedChange change, @NotNull Map context) {
+ @Nullable
+ public static ChangeDiffRequestChain.Producer createDiffRequestProducer(@NotNull Project project,
+ @NotNull Change change,
+ @NotNull Map context,
+ boolean forDiffPreview) {
+ if (change instanceof MergedChange) {
+ MergedChange mergedChange = (MergedChange)change;
+ if (mergedChange.getSourceChanges().size() == 2) {
+ if (forDiffPreview) {
+ putFilePathsIntoMergedChangeContext(mergedChange, context);
+ }
+ return new MergedChangeDiffRequestProvider.MyProducer(project, mergedChange, context);
+ }
+ }
+
+ if (forDiffPreview) {
+ putFilePathsIntoChangeContext(change, context);
+ }
+
+ return ChangeDiffRequestProducer.create(project, change, context);
+ }
+
+ private void putRootTagIntoChangeContext(@NotNull Change change, @NotNull Map context) {
+ CommitId parentId = null;
+ for (CommitId commitId : myChangesToParents.keySet()) {
+ if (myChangesToParents.get(commitId).contains(change)) {
+ parentId = commitId;
+ break;
+ }
+ }
+
+ if (parentId != null) {
+ RootTag tag = new RootTag(parentId.getHash(), getText(parentId));
+ context.put(ChangeDiffRequestProducer.TAG_KEY, tag);
+ }
+ }
+
+ private static void putFilePathsIntoMergedChangeContext(@NotNull MergedChange change, @NotNull Map context) {
ContentRevision centerRevision = change.getAfterRevision();
ContentRevision leftRevision = change.getSourceChanges().get(0).getBeforeRevision();
ContentRevision rightRevision = change.getSourceChanges().get(1).getBeforeRevision();
@@ -305,7 +317,7 @@ class VcsLogChangesBrowser extends ChangesBrowserBase implements Disposable {
context.put(VCS_DIFF_LEFT_CONTENT_TITLE, getRevisionTitle(leftRevision, leftFile, centerFile == null ? rightFile : centerFile));
}
- private static void putFilePathsIntoContext(@NotNull Change change, @NotNull Map context) {
+ private static void putFilePathsIntoChangeContext(@NotNull Change change, @NotNull Map context) {
ContentRevision afterRevision = change.getAfterRevision();
ContentRevision beforeRevision = change.getBeforeRevision();
FilePath aFile = afterRevision == null ? null : afterRevision.getFile();