mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[file-history] introduce diff preview
IDEA-170988
This commit is contained in:
@@ -156,6 +156,7 @@
|
||||
<reference id="Vcs.Log.ShowOtherBranches"/>
|
||||
<separator/>
|
||||
<reference id="Vcs.Log.ShowDetailsAction"/>
|
||||
<reference id="Vcs.Log.ShowDiffPreview"/>
|
||||
<separator/>
|
||||
<reference id="VcsHistoryActionsGroup.Toolbar"/>
|
||||
<separator/>
|
||||
@@ -195,6 +196,7 @@
|
||||
<reference id="Vcs.Log.ShowOtherBranches"/>
|
||||
<reference id="Vcs.Log.Refresh"/>
|
||||
<reference id="Vcs.Log.ShowDetailsAction"/>
|
||||
<reference id="Vcs.Log.ShowDiffPreview"/>
|
||||
<reference id="Vcs.FileHistory.ContextMenu"/>
|
||||
<reference id="Vcs.FileHistory.Toolbar"/>
|
||||
<reference id="VcsHistoryActionsGroup"/>
|
||||
|
||||
@@ -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<ChangeViewDiffRequestProcessor.Wrapper> = allChanges
|
||||
|
||||
override fun getAllChanges(): List<ChangeViewDiffRequestProcessor.Wrapper> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -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<Integer, Integer> COLUMN_WIDTH = ContainerUtil.newHashMap();
|
||||
public List<Integer> COLUMN_ORDER = ContainerUtil.newArrayList();
|
||||
public boolean SHOW_DIFF_PREVIEW = true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T get(@NotNull VcsLogUiProperty<T> 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<Integer> 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 <T> void set(@NotNull VcsLogUiProperty<T> 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<Integer>)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 <T> boolean exists(@NotNull VcsLogUiProperty<T> 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;
|
||||
}
|
||||
|
||||
|
||||
+16
-1
@@ -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<Boolean> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CommitId, VcsShortCommitDetails> getter,
|
||||
@NotNull Disposable parent) {
|
||||
@NotNull MainVcsLogUiProperties uiProperties,
|
||||
@NotNull Function<CommitId, VcsShortCommitDetails> 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<Key, Object> 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<Key, Object> context) {
|
||||
@Nullable
|
||||
public static ChangeDiffRequestChain.Producer createDiffRequestProducer(@NotNull Project project,
|
||||
@NotNull Change change,
|
||||
@NotNull Map<Key, Object> 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<Key, Object> 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<Key, Object> 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<Key, Object> context) {
|
||||
private static void putFilePathsIntoChangeContext(@NotNull Change change, @NotNull Map<Key, Object> context) {
|
||||
ContentRevision afterRevision = change.getAfterRevision();
|
||||
ContentRevision beforeRevision = change.getBeforeRevision();
|
||||
FilePath aFile = afterRevision == null ? null : afterRevision.getFile();
|
||||
|
||||
Reference in New Issue
Block a user