mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user