[vcs-log] load full details in background

1. log actions and cherry-pick load details in backgound task in action performed
2. deal with select all problem IDEA-118190
This commit is contained in:
Julia Beliaeva
2016-01-14 19:25:51 +03:00
parent 75425014da
commit 947b4537d7
11 changed files with 329 additions and 150 deletions
@@ -21,6 +21,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
@@ -32,9 +33,9 @@ import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ChangeListManagerEx;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsLog;
@@ -57,39 +58,17 @@ public class VcsCherryPickAction extends DumbAwareAction {
@Override
public void actionPerformed(AnActionEvent e) {
FileDocumentManager.getInstance().saveAllDocuments();
final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
VcsLog log = e.getRequiredData(VcsLogDataKeys.VCS_LOG);
final List<VcsFullCommitDetails> commits = VcsLogUtil.collectLoadedSelectedDetails(log, false);
for (VcsFullCommitDetails commit : commits) {
myIdsInProgress.add(commit.getId());
}
FileDocumentManager.getInstance().saveAllDocuments();
final ChangeListManagerEx changeListManagerEx = (ChangeListManagerEx)ChangeListManager.getInstance(project);
changeListManagerEx.blockModalNotifications();
new Task.Backgroundable(project, "Cherry-picking", false) {
public void run(@NotNull ProgressIndicator indicator) {
try {
List<VcsFullCommitDetails> sortedCommits = sortCommits(commits);
Map<VcsCherryPicker, List<VcsFullCommitDetails>> groupedCommits = groupByVcs(project, sortedCommits);
for (Map.Entry<VcsCherryPicker, List<VcsFullCommitDetails>> entry : groupedCommits.entrySet()) {
entry.getKey().cherryPick(entry.getValue());
}
}
finally {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
changeListManagerEx.unblockModalNotifications();
for (VcsFullCommitDetails commit : commits) {
myIdsInProgress.remove(commit.getId());
}
}
});
}
log.requestSelectedDetails(new Consumer<Set<VcsFullCommitDetails>>() {
@Override
public void consume(Set<VcsFullCommitDetails> details) {
ProgressManager.getInstance().run(new CherryPickingTask(project, details));
}
}.queue();
}, null);
}
/**
@@ -101,27 +80,6 @@ public class VcsCherryPickAction extends DumbAwareAction {
return commits;
}
private static Map<VcsCherryPicker, List<VcsFullCommitDetails>> groupByVcs(@NotNull Project project,
@NotNull List<VcsFullCommitDetails> commits) {
final ProjectLevelVcsManager projectLevelVcsManager = ProjectLevelVcsManager.getInstance(project);
Map<VcsCherryPicker, List<VcsFullCommitDetails>> resultMap = new HashMap<VcsCherryPicker, List<VcsFullCommitDetails>>();
for (VcsFullCommitDetails commit : commits) {
VcsCherryPicker cherryPicker = getCherryPickerForCommit(project, projectLevelVcsManager, commit);
if (cherryPicker == null) {
VcsNotifier.getInstance(project).notifyWeakError(
"Cherry pick is not supported for commit " + commit.getId().toShortString() + " from root " + commit.getRoot().getName());
return Collections.emptyMap();
}
List<VcsFullCommitDetails> list = resultMap.get(cherryPicker);
if (list == null) {
resultMap.put(cherryPicker, list = new ArrayList<VcsFullCommitDetails>()); // ordered set!!
}
list.add(commit);
}
return resultMap;
}
@Nullable
private static VcsCherryPicker getCherryPickerFor(@NotNull Project project, @NotNull final VcsKey key) {
return ContainerUtil.find(Extensions.getExtensions(VcsCherryPicker.EXTENSION_POINT_NAME, project), new Condition<VcsCherryPicker>() {
@@ -191,4 +149,67 @@ public class VcsCherryPickAction extends DumbAwareAction {
VcsKey key = vcs.getKeyInstanceMethod();
return getCherryPickerFor(project, key);
}
private class CherryPickingTask extends Task.Backgroundable {
private final Project myProject;
private final ProjectLevelVcsManager myProjectLevelVcsManager;
private final Map<VcsCherryPicker, List<VcsFullCommitDetails>> myGroupedCommits = ContainerUtil.newHashMap();
private final Collection<VcsFullCommitDetails> myAllCommits;
private final ChangeListManagerEx myChangeListManagerEx;
public CherryPickingTask(@NotNull Project project, @NotNull Set<VcsFullCommitDetails> details) {
super(project, "Cherry-Picking");
myProject = project;
myProjectLevelVcsManager = ProjectLevelVcsManager.getInstance(myProject);
myAllCommits = details;
myChangeListManagerEx = (ChangeListManagerEx)ChangeListManager.getInstance(myProject);
myChangeListManagerEx.blockModalNotifications();
}
public boolean processDetails(@NotNull VcsFullCommitDetails details) {
myIdsInProgress.add(details.getId());
VcsCherryPicker cherryPicker = getCherryPickerForCommit(myProject, myProjectLevelVcsManager, details);
if (cherryPicker == null) {
VcsNotifier.getInstance(myProject).notifyWeakError(
"Cherry pick is not supported for commit " + details.getId().toShortString() + " from root " + details.getRoot().getName());
return false;
}
List<VcsFullCommitDetails> list = myGroupedCommits.get(cherryPicker);
if (list == null) {
myGroupedCommits.put(cherryPicker, list = new ArrayList<VcsFullCommitDetails>()); // ordered set!!
}
list.add(details);
return true;
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
boolean isOk = true;
for (VcsFullCommitDetails details : myAllCommits) {
if (!processDetails(details)) {
isOk = false;
break;
}
}
if (isOk) {
for (Map.Entry<VcsCherryPicker, List<VcsFullCommitDetails>> entry : myGroupedCommits.entrySet()) {
entry.getKey().cherryPick(sortCommits(entry.getValue()));
}
}
}
finally {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
myChangeListManagerEx.unblockModalNotifications();
for (VcsFullCommitDetails commit : myAllCommits) {
myIdsInProgress.remove(commit.getId());
}
}
});
}
}
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.vcs.log.VcsFullCommitDetails;
@@ -34,17 +35,24 @@ import com.intellij.vcs.log.impl.VcsLogUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Collection;
import java.util.Set;
public abstract class VcsLogAction<Repo extends Repository> extends DumbAwareAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getRequiredData(CommonDataKeys.PROJECT);
final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
VcsLog log = e.getRequiredData(VcsLogDataKeys.VCS_LOG);
MultiMap<Repo, VcsFullCommitDetails> grouped = getGroupedCommits(project, log, false);
if (grouped == null) return;
actionPerformed(project, grouped);
log.requestSelectedDetails(new Consumer<Set<VcsFullCommitDetails>>() {
@Override
public void consume(Set<VcsFullCommitDetails> details) {
MultiMap<Repo, VcsFullCommitDetails> grouped = groupCommits(project, details, false);
if (grouped == null) return;
actionPerformed(project, grouped);
}
}, null);
}
@Override
@@ -88,7 +96,13 @@ public abstract class VcsLogAction<Repo extends Repository> extends DumbAwareAct
@Nullable
private MultiMap<Repo, VcsFullCommitDetails> getGroupedCommits(@NotNull Project project, @NotNull VcsLog log, boolean fromUpdate) {
List<VcsFullCommitDetails> commits = VcsLogUtil.collectLoadedSelectedDetails(log, fromUpdate);
return groupCommits(project, VcsLogUtil.collectLoadedSelectedDetails(log, fromUpdate), fromUpdate);
}
@Nullable
private MultiMap<Repo, VcsFullCommitDetails> groupCommits(@NotNull Project project,
Collection<VcsFullCommitDetails> commits,
boolean fromUpdate) {
MultiMap<Repo, VcsFullCommitDetails> map = MultiMap.create();
for (VcsFullCommitDetails commit : commits) {
Repo root = getRepositoryForRoot(project, commit.getRoot());
@@ -15,6 +15,7 @@
*/
package com.intellij.vcsUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
@@ -61,10 +62,8 @@ public class VcsFileUtil {
List<List<String>> chunks = chunkArguments(arguments);
for (List<String> chunk : chunks) {
if (ProgressManager.getInstance().getProgressIndicator() != null &&
ProgressManager.getInstance().getProgressIndicator().isCanceled()) {
return result;
}
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator != null) indicator.checkCanceled();
result.addAll(processor.fun(chunk));
}
@@ -15,12 +15,15 @@
*/
package com.intellij.vcs.log;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
/**
@@ -41,6 +44,16 @@ public interface VcsLog {
@NotNull
List<VcsFullCommitDetails> getSelectedDetails();
/**
* Sends a request to load details that are currently selected.
* Details are loaded in background. If a progress indicator is specified it is used during loading process.
* After all details are loaded they are provided to the consumer in the EDT.
*
* @param consumer called in EDT after all details are loaded.
* @param indicator progress indicator to use in loading process, can be null.
*/
void requestSelectedDetails(@NotNull Consumer<Set<VcsFullCommitDetails>> consumer, @Nullable ProgressIndicator indicator);
/**
* Returns names of branches which contain the given commit, or null if this information is unavailable.
*/
@@ -1,10 +1,16 @@
package com.intellij.vcs.log.data;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.containers.ContainerUtil;
@@ -20,23 +26,22 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.*;
import java.util.List;
import java.util.Map;
/**
* The DataGetter realizes the following pattern of getting some data (parametrized by {@code T}) from the VCS:
* <ul>
* <li>it tries to get it from the cache;</li>
* <li>if it fails, it tries to get it from the VCS, and additionally loads several commits around the requested one,
* to avoid querying the VCS if user investigates details of nearby commits.</li>
* <li>The loading happens asynchronously: a fake {@link LoadingDetails} object is returned </li>
* <li>it tries to get it from the cache;</li>
* <li>if it fails, it tries to get it from the VCS, and additionally loads several commits around the requested one,
* to avoid querying the VCS if user investigates details of nearby commits.</li>
* <li>The loading happens asynchronously: a fake {@link LoadingDetails} object is returned </li>
* </ul>
*
* @author Kirill Likhodedov
*/
abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Disposable, DataGetter<T> {
private static final Logger LOG = Logger.getInstance(AbstractDataGetter.class);
private static final int UP_PRELOAD_COUNT = 20;
private static final int DOWN_PRELOAD_COUNT = 40;
@@ -62,19 +67,23 @@ abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Di
myLogProviders = logProviders;
myCache = cache;
Disposer.register(parentDisposable, this);
myLoader = new SequentialLimitedLifoExecutor<TaskDescriptor>(this, MAX_LOADING_TASKS,
new ThrowableConsumer<TaskDescriptor, VcsException>() {
myLoader =
new SequentialLimitedLifoExecutor<TaskDescriptor>(this, MAX_LOADING_TASKS, new ThrowableConsumer<TaskDescriptor, VcsException>() {
@Override
public void consume(final TaskDescriptor task) throws VcsException {
preLoadCommitData(task.myCommits);
notifyLoaded();
}
});
}
private void notifyLoaded() {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void consume(TaskDescriptor task) throws VcsException {
preLoadCommitData(task.myCommits);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
for (Runnable loadingFinishedListener : myLoadingFinishedListeners) {
loadingFinishedListener.run();
}
}
});
public void run() {
for (Runnable loadingFinishedListener : myLoadingFinishedListeners) {
loadingFinishedListener.run();
}
}
});
}
@@ -85,7 +94,7 @@ abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Di
}
@Override
@Nullable
@NotNull
public T getCommitData(int row, @NotNull GraphTableModel tableModel) {
assert EventQueue.isDispatchThread();
Integer hash = tableModel.getIdAtRow(row);
@@ -93,8 +102,104 @@ abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Di
if (details != null) {
return details;
}
runLoadAroundCommitData(row, tableModel);
return myCache.get(hash); // now it is in the cache as "Loading Details".
runLoadCommitsData(tableModel, createRowsIterable(row, UP_PRELOAD_COUNT, DOWN_PRELOAD_COUNT, tableModel.getRowCount()));
T result = myCache.get(hash);
assert result != null; // now it is in the cache as "Loading Details" (runLoadCommitsData puts it there)
return result;
}
private static Iterable<Integer> createRowsIterable(final int row, final int above, final int below, final int maxRows) {
return new Iterable<Integer>() {
@NotNull
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int myIndex = Math.max(0, row - above);
@Override
public boolean hasNext() {
return myIndex < row + below && myIndex < maxRows;
}
@Override
public Integer next() {
int next = myIndex;
myIndex++;
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Removing elements is not supported.");
}
};
}
};
}
@Override
public void loadCommitsData(@NotNull List<Integer> rows,
@NotNull GraphTableModel tableModel,
@NotNull Consumer<Set<T>> consumer,
@Nullable ProgressIndicator indicator) {
assert EventQueue.isDispatchThread();
loadCommitsData(getCommitsForRows(rows, tableModel), consumer, indicator);
}
private void loadCommitsData(@NotNull final MultiMap<VirtualFile, Integer> commits,
@NotNull final Consumer<Set<T>> consumer,
@Nullable ProgressIndicator indicator) {
final Set<T> result = ContainerUtil.newHashSet();
final MultiMap<VirtualFile, Integer> toLoad = MultiMap.create();
long taskNumber = myCurrentTaskIndex++;
for (VirtualFile root : commits.keySet()) {
Collection<Integer> hashesForRoot = commits.get(root);
for (final Integer commitId : hashesForRoot) {
T details = getFromCache(commitId);
if (details == null || details instanceof LoadingDetails) {
toLoad.putValue(root, commitId);
cacheCommit(commitId, root, taskNumber);
}
else {
result.add(details);
}
}
}
if (toLoad.isEmpty()) {
consumer.consume(result);
}
else {
Task.Backgroundable task =
new Task.Backgroundable(null, "Loading Selected Details", true, PerformInBackgroundOption.ALWAYS_BACKGROUND) {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
indicator.checkCanceled();
try {
result.addAll(preLoadCommitData(toLoad));
notifyLoaded();
}
catch (VcsException e) {
LOG.error(e);
}
}
@Override
public void onSuccess() {
consumer.consume(result);
}
};
if (indicator != null) {
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, indicator);
}
else {
ProgressManager.getInstance().run(task);
}
}
}
@Override
@@ -125,47 +230,48 @@ abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Di
@Nullable
protected abstract T getFromAdditionalCache(int commitId);
private void runLoadAroundCommitData(int row, @NotNull GraphTableModel tableModel) {
private void runLoadCommitsData(@NotNull GraphTableModel tableModel, @NotNull Iterable<Integer> rows) {
long taskNumber = myCurrentTaskIndex++;
MultiMap<VirtualFile, Integer> commits = getCommitsAround(row, tableModel, UP_PRELOAD_COUNT, DOWN_PRELOAD_COUNT);
MultiMap<VirtualFile, Integer> commits = getCommitsForRows(rows, tableModel);
for (Map.Entry<VirtualFile, Collection<Integer>> hashesByRoots : commits.entrySet()) {
VirtualFile root = hashesByRoots.getKey();
Collection<Integer> hashes = hashesByRoots.getValue();
// fill the cache with temporary "Loading" values to avoid producing queries for each commit that has not been cached yet,
// even if it will be loaded within a previous query
for (final int commitId : hashes) {
if (!myCache.isKeyCached(commitId)) {
myCache.put(commitId, (T)new LoadingDetails(new Computable<Hash>(){
@Override
public Hash compute() {
return myHashMap.getCommitId(commitId).getHash();
}
}, taskNumber, root));
}
cacheCommit(commitId, root, taskNumber);
}
}
TaskDescriptor task = new TaskDescriptor(commits);
myLoader.queue(task);
myLoader.queue(new TaskDescriptor(commits));
}
private void cacheCommit(final int commitId, VirtualFile root, long taskNumber) {
// fill the cache with temporary "Loading" values to avoid producing queries for each commit that has not been cached yet,
// even if it will be loaded within a previous query
if (!myCache.isKeyCached(commitId)) {
myCache.put(commitId, (T)new LoadingDetails(new Computable<Hash>() {
@Override
public Hash compute() {
return myHashMap.getCommitId(commitId).getHash();
}
}, taskNumber, root));
}
}
@NotNull
private static MultiMap<VirtualFile, Integer> getCommitsAround(int selectedRow,
@NotNull GraphTableModel model,
int above,
int below) {
private static MultiMap<VirtualFile, Integer> getCommitsForRows(@NotNull Iterable<Integer> rows, @NotNull GraphTableModel model) {
MultiMap<VirtualFile, Integer> commits = MultiMap.create();
for (int row = Math.max(0, selectedRow - above); row < selectedRow + below && row < model.getRowCount(); row++) {
Integer hash = model.getIdAtRow(row);
for (int row : rows) {
Integer commitId = model.getIdAtRow(row);
VirtualFile root = model.getRoot(row);
commits.putValue(root, hash);
commits.putValue(root, commitId);
}
return commits;
}
private void preLoadCommitData(@NotNull MultiMap<VirtualFile, Integer> commits) throws VcsException {
private Set<T> preLoadCommitData(@NotNull MultiMap<VirtualFile, Integer> commits) throws VcsException {
Set<T> result = ContainerUtil.newHashSet();
for (Map.Entry<VirtualFile, Collection<Integer>> entry : commits.entrySet()) {
List<String> hashStrings = ContainerUtil.map(entry.getValue(), new Function<Integer, String>() {
@Override
@@ -174,8 +280,10 @@ abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Di
}
});
List<? extends T> details = readDetails(myLogProviders.get(entry.getKey()), entry.getKey(), hashStrings);
result.addAll(details);
saveInCache(details);
}
return result;
}
public void saveInCache(final List<? extends T> details) {
@@ -202,11 +310,10 @@ abstract class AbstractDataGetter<T extends VcsShortCommitDetails> implements Di
}
private static class TaskDescriptor {
private final MultiMap<VirtualFile, Integer> myCommits;
@NotNull private final MultiMap<VirtualFile, Integer> myCommits;
private TaskDescriptor(MultiMap<VirtualFile, Integer> commits) {
private TaskDescriptor(@NotNull MultiMap<VirtualFile, Integer> commits) {
myCommits = commits;
}
}
}
@@ -15,15 +15,25 @@
*/
package com.intellij.vcs.log.data;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.Consumer;
import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
public interface DataGetter<T extends VcsShortCommitDetails> {
@Nullable
T getCommitData(int row, @NotNull GraphTableModel tableModel);
void loadCommitsData(@NotNull List<Integer> rows,
@NotNull GraphTableModel tableModel,
@NotNull Consumer<Set<T>> consumer,
@Nullable ProgressIndicator indicator);
@Nullable
T getCommitDataIfAvailable(int hash);
}
@@ -15,7 +15,11 @@
*/
package com.intellij.vcs.log.impl;
import com.google.common.primitives.Ints;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Condition;
import com.intellij.ui.table.JBTable;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.VcsLogDataHolder;
@@ -25,9 +29,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.*;
import java.util.List;
import java.util.concurrent.Future;
@@ -35,6 +37,7 @@ import java.util.concurrent.Future;
*
*/
public class VcsLogImpl implements VcsLog {
private static final int SLICE_SIZE = 50;
@NotNull private final VcsLogDataHolder myDataHolder;
@NotNull private final VcsLogUiImpl myUi;
@@ -79,6 +82,12 @@ public class VcsLogImpl implements VcsLog {
};
}
@Override
public void requestSelectedDetails(@NotNull Consumer<Set<VcsFullCommitDetails>> consumer, @Nullable ProgressIndicator indicator) {
List<Integer> rowsList = Ints.asList(myUi.getTable().getSelectedRows());
myDataHolder.getCommitDetailsGetter().loadCommitsData(rowsList, (GraphTableModel)myUi.getTable().getModel(), consumer, indicator);
}
@Nullable
@Override
public Collection<String> getContainingBranches(@NotNull Hash commitHash) {
@@ -168,10 +168,6 @@ class DetailsPanel extends JPanel implements ListSelectionListener {
int row = rows[0];
GraphTableModel tableModel = (GraphTableModel)myGraphTable.getModel();
VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(row, tableModel);
if (commitData == null) {
showMessage("No commits selected");
return;
}
if (commitData instanceof LoadingDetails) {
myLoadingPanel.startLoading();
myCommitDetailsPanel.setData(null);
@@ -4,6 +4,8 @@ import com.intellij.icons.AllIcons;
import com.intellij.ide.actions.RefreshAction;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
@@ -12,6 +14,7 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.TextRevisionNumber;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
import com.intellij.openapi.vcs.changes.committed.RepositoryChangesBrowser;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
@@ -19,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
@@ -44,6 +48,7 @@ import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.Set;
import static com.intellij.util.ObjectUtils.assertNotNull;
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
@@ -154,7 +159,6 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
myLogDataHolder.getCommitDetailsGetter().addDetailsLoadedListener(new Runnable() {
@Override
public void run() {
selectionChangeListener.valueChanged(null);
myDetailsPanel.valueChanged(null);
}
});
@@ -291,11 +295,6 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
myBranchesPanel.setVisible(visible);
}
@Nullable
public List<Change> getSelectedChanges() {
return myGraphTable.getSelectedChanges();
}
@Override
public void calcData(DataKey key, DataSink sink) {
if (VcsLogDataKeys.VCS_LOG == key) {
@@ -308,7 +307,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
sink.put(key, myLogDataHolder);
}
else if (VcsDataKeys.CHANGES == key || VcsDataKeys.SELECTED_CHANGES == key) {
List<Change> selectedChanges = getSelectedChanges();
List<Change> selectedChanges = myGraphTable.getSelectedChanges();
if (selectedChanges != null) {
sink.put(key, ArrayUtil.toObjectArray(selectedChanges, Change.class));
}
@@ -363,6 +362,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
private class CommitSelectionListener implements ListSelectionListener {
private final ChangesBrowser myChangesBrowser;
private ProgressIndicator myLastRequest;
public CommitSelectionListener(ChangesBrowser changesBrowser) {
myChangesBrowser = changesBrowser;
@@ -371,23 +371,38 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
@Override
public void valueChanged(@Nullable ListSelectionEvent event) {
if (event != null && event.getValueIsAdjusting()) return;
if (myLastRequest != null) myLastRequest.cancel();
myLastRequest = null;
int rows = getGraphTable().getSelectedRowCount();
if (rows < 1 || rows > MAX_SELECTED_COMMITS) {
if (rows < 1) {
myChangesLoadingPane.stopLoading();
myChangesBrowser.getViewer().setEmptyText(rows < 1 ? "" : "Too many commits selected.");
myChangesBrowser.getViewer().setEmptyText("");
myChangesBrowser.setChangesToDisplay(Collections.<Change>emptyList());
}
else {
List<Change> selectedChanges = getSelectedChanges();
if (selectedChanges != null) {
myChangesLoadingPane.stopLoading();
myChangesBrowser.setChangesToDisplay(selectedChanges);
}
else {
myChangesBrowser.setChangesToDisplay(Collections.<Change>emptyList());
setDefaultEmptyText(myChangesBrowser);
myChangesLoadingPane.startLoading();
}
myChangesBrowser.setChangesToDisplay(Collections.<Change>emptyList());
setDefaultEmptyText(myChangesBrowser);
myChangesLoadingPane.startLoading();
final EmptyProgressIndicator indicator = new EmptyProgressIndicator();
myLastRequest = indicator;
myLog.requestSelectedDetails(new Consumer<Set<VcsFullCommitDetails>>() {
@Override
public void consume(Set<VcsFullCommitDetails> detailsList) {
if (myLastRequest == indicator && !(indicator.isCanceled())) {
myLastRequest = null;
List<Change> changes = ContainerUtil.newArrayList();
for (VcsFullCommitDetails details : detailsList) {
changes.addAll(details.getChanges());
}
changes = CommittedChangesTreeBrowser.zipChanges(changes);
myChangesLoadingPane.stopLoading();
myChangesBrowser.setChangesToDisplay(changes);
}
}
}, indicator);
}
}
}
@@ -152,16 +152,6 @@ public class GraphTableModel extends AbstractTableModel {
return COLUMN_COUNT;
}
@Nullable
private VcsShortCommitDetails getShortDetails(int rowIndex) {
return myLogDataHolder.getMiniDetailsGetter().getCommitData(rowIndex, this);
}
@Nullable
public VcsFullCommitDetails getFullCommitDetails(int rowIndex) {
return myLogDataHolder.getCommitDetailsGetter().getCommitData(rowIndex, this);
}
/**
* Requests the proper data provider to load more data from the log & recreate the model.
*
@@ -180,7 +170,7 @@ public class GraphTableModel extends AbstractTableModel {
requestToLoadMore(EmptyRunnable.INSTANCE);
}
VcsShortCommitDetails data = getShortDetails(rowIndex);
VcsShortCommitDetails data = myLogDataHolder.getMiniDetailsGetter().getCommitData(rowIndex, this);
switch (columnIndex) {
case ROOT_COLUMN:
return getRoot(rowIndex);
@@ -225,8 +215,8 @@ public class GraphTableModel extends AbstractTableModel {
public List<Change> getSelectedChanges(@NotNull List<Integer> selectedRows) {
List<Change> changes = new ArrayList<Change>();
for (int row : selectedRows) {
VcsFullCommitDetails commitData = getFullCommitDetails(row);
if (commitData == null || commitData instanceof LoadingDetails) {
VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(row, this);
if (commitData instanceof LoadingDetails) {
return null;
}
changes.addAll(commitData.getChanges());
@@ -16,8 +16,10 @@
package com.intellij.vcs.log.data
import com.intellij.mock.MockVirtualFile
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.util.Condition
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.Consumer
import com.intellij.util.Function
import com.intellij.vcs.log.*
import com.intellij.vcs.log.graph.GraphCommit
@@ -149,11 +151,14 @@ class VisiblePackBuilderTest {
val commitDetailsGetter = object : DataGetter<VcsFullCommitDetails> {
override fun getCommitData(row: Int, tableModel: GraphTableModel): VcsFullCommitDetails? {
return null;
return null
}
override fun loadCommitsData(rows: MutableList<Int>, tableModel: GraphTableModel, consumer: Consumer<MutableSet<VcsFullCommitDetails>>, indicator: ProgressIndicator?) {
}
override fun getCommitDataIfAvailable(hash: Int): VcsFullCommitDetails? {
return null;
return null
}
}
val builder = VisiblePackBuilder(providers, hashMap, detailsCache, commitDetailsGetter)