Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2013-10-28 01:50:32 +04:00
15 changed files with 184 additions and 76 deletions
@@ -201,7 +201,7 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry {
* In some cases we can do highlighting in annotator or high. visitor based on global inspection or use a shared local inspection tool
*/
public boolean worksInBatchModeOnly() {
return getSharedLocalInspectionTool() != null;
return getSharedLocalInspectionTool() == null;
}
/**
@@ -30,7 +30,16 @@ public interface VcsLog {
* Returns commits currently selected in the log.
*/
@NotNull
List<VcsFullCommitDetails> getSelectedCommits();
List<Hash> getSelectedCommits();
/**
* Returns details of the given commit, if they have been already loaded.
* In most cases they are already in the cache, and will be returned.
* Otherwise null is returned.
* Asynchronous loading of the details which are not yet available is done automatically from the log table component.
*/
@Nullable
VcsFullCommitDetails getDetailsIfAvailable(@NotNull Hash hash);
/**
* Returns names of branches which contain the given commit, or null if this information is unavailable.
@@ -1,6 +1,9 @@
package com.intellij.vcs.log.graph.render;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
@@ -11,8 +14,14 @@ public class CommitCell {
private final String text;
private final Collection<VcsRef> refsToThisCommit;
private Hash myHash;
public CommitCell(String text, Collection<VcsRef> refsToThisCommit) {
/**
* Hash can be null, if, for example, this is a cell which doesn't contain a commit, but contains only a part of the graph
* (such situations may appear, for example, if graph is filtered by branch, as described in IDEA-115442).
*/
public CommitCell(@Nullable Hash hash, @NotNull String text, @NotNull Collection<VcsRef> refsToThisCommit) {
myHash = hash;
this.text = text;
this.refsToThisCommit = refsToThisCommit;
}
@@ -25,4 +34,8 @@ public class CommitCell {
return refsToThisCommit;
}
@Nullable
public Hash getHash() {
return myHash;
}
}
@@ -1,7 +1,10 @@
package com.intellij.vcs.log.graph.render;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.printmodel.GraphPrintCell;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
@@ -13,8 +16,9 @@ public class GraphCommitCell extends CommitCell {
private final GraphPrintCell row;
public GraphCommitCell(GraphPrintCell row, String text, Collection<VcsRef> refsToThisCommit) {
super(text, refsToThisCommit);
public GraphCommitCell(@Nullable Hash hash, @NotNull GraphPrintCell row, @NotNull String text,
@NotNull Collection<VcsRef> refsToThisCommit) {
super(hash, text, refsToThisCommit);
this.row = row;
}
@@ -38,11 +38,17 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
private static final int UP_PRELOAD_COUNT = 20;
private static final int DOWN_PRELOAD_COUNT = 40;
private static final int MAX_LOADING_TASKS = 10;
@NotNull protected final VcsLogDataHolder myDataHolder;
@NotNull private final Map<VirtualFile, VcsLogProvider> myLogProviders;
@NotNull private final VcsCommitCache<T> myCache;
/**
* The sequence number of the current "loading" task.
*/
private long myCurrentTaskIndex = 0;
@NotNull private final QueueProcessor<TaskDescriptor> myLoader = new QueueProcessor<TaskDescriptor>(new DetailsLoadingTask());
@NotNull private final Collection<Runnable> myLoadingFinishedListeners = new ArrayList<Runnable>();
@@ -64,18 +70,50 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
public T getCommitData(@NotNull final Node node) {
assert EventQueue.isDispatchThread();
Hash hash = node.getCommitHash();
T details = getFromCache(hash);
if (details != null) {
return details;
}
return loadingDetails(node, hash);
}
@NotNull
private T loadingDetails(Node node, Hash hash) {
TaskDescriptor descriptor = runLoadAroundCommitData(node);
T loadingDetails = (T)new LoadingDetails(hash, descriptor.getTaskNum());
return loadingDetails;
}
@NotNull
public T getCommitData(@NotNull Hash hash) {
assert EventQueue.isDispatchThread();
T details = getFromCache(hash);
if (details != null) {
return details;
}
Node node = myDataHolder.getDataPack().getNodeByHash(hash); // TODO this may possibly be slow => need to add to the Task as well
return loadingDetails(node, hash);
}
@Nullable
public T getCommitDataIfAvailable(@NotNull Hash hash) {
return getFromCache(hash);
}
@Nullable
private T getFromCache(@NotNull Hash hash) {
T details = myCache.get(hash);
if (details != null) {
if (details instanceof LoadingDetails) {
if (((LoadingDetails)details).getLoadingTaskIndex() <= myCurrentTaskIndex - MAX_LOADING_TASKS) {
// don't let old "loading" requests stay in the cache forever
myCache.remove(hash);
return null;
}
}
return details;
}
details = (T)myDataHolder.getTopCommitDetails(hash);
if (details != null) {
return details;
}
T loadingDetails = (T)new LoadingDetails(hash);
runLoadAroundCommitData(node);
return loadingDetails;
return (T)myDataHolder.getTopCommitDetails(hash);
}
@Nullable
@@ -93,9 +131,11 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
return null;
}
private void runLoadAroundCommitData(@NotNull Node node) {
@NotNull
private TaskDescriptor runLoadAroundCommitData(@NotNull Node node) {
int rowIndex = node.getRowIndex();
List<Node> nodes = new ArrayList<Node>();
long taskNumber = myCurrentTaskIndex++;
for (int i = rowIndex - UP_PRELOAD_COUNT; i < rowIndex + DOWN_PRELOAD_COUNT; i++) {
Node commitNode = getCommitNodeInRow(i);
if (commitNode != null) {
@@ -105,11 +145,13 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
// 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(hash)) {
myCache.put(hash, (T)new LoadingDetails(hash));
myCache.put(hash, (T)new LoadingDetails(hash, taskNumber));
}
}
}
myLoader.addFirst(new TaskDescriptor(nodes));
TaskDescriptor task = new TaskDescriptor(nodes, taskNumber);
myLoader.addFirst(task);
return task;
}
private void preLoadCommitData(@NotNull List<Node> nodes) throws VcsException {
@@ -150,19 +192,24 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
private static class TaskDescriptor {
private final List<Node> nodes;
private long myTaskNum;
private TaskDescriptor(List<Node> nodes) {
private TaskDescriptor(List<Node> nodes, long taskNum) {
this.nodes = nodes;
myTaskNum = taskNum;
}
public long getTaskNum() {
return myTaskNum;
}
}
private class DetailsLoadingTask implements Consumer<TaskDescriptor> {
private static final int MAX_LOADINGS = 10;
@Override
public void consume(final TaskDescriptor task) {
try {
myLoader.dismissLastTasks(MAX_LOADINGS);
myLoader.dismissLastTasks(MAX_LOADING_TASKS);
preLoadCommitData(task.nodes);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
@@ -16,9 +16,16 @@ import java.util.Collections;
*/
public class LoadingDetails extends VcsFullCommitDetailsImpl {
public LoadingDetails(@NotNull Hash hash) {
private final long myLoadingTaskIndex;
public LoadingDetails(@NotNull Hash hash, long loadingTaskIndex) {
super(hash, Collections.<Hash>emptyList(), -1, AbstractVcsLogTableModel.UNKNOWN_ROOT,
"Loading...", "", "", "", "", "", -1, Collections.<Change>emptyList());
myLoadingTaskIndex = loadingTaskIndex;
}
public long getLoadingTaskIndex() {
return myLoadingTaskIndex;
}
}
@@ -51,4 +51,9 @@ class VcsCommitCache<T extends VcsShortCommitDetails> {
assert EventQueue.isDispatchThread();
return myCache.get(hash);
}
public void remove(@NotNull Hash hash) {
myCache.remove(hash);
}
}
@@ -440,6 +440,10 @@ public class VcsLogDataHolder implements Disposable {
return detail;
}
});
myDetailsGetter.saveInCache(list);
myMiniDetailsGetter.saveInCache(list);
invokeAndWait(new Runnable() {
@Override
public void run() {
@@ -15,14 +15,15 @@
*/
package com.intellij.vcs.log.impl;
import com.intellij.ui.table.JBTable;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsLog;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.graph.elements.Node;
import com.intellij.vcs.log.graph.render.CommitCell;
import com.intellij.vcs.log.ui.VcsLogUI;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -50,17 +51,25 @@ public class VcsLogImpl implements VcsLog {
return myDataHolder != null && myUi != null;
}
@NotNull
@Override
public List<VcsFullCommitDetails> getSelectedCommits() {
List<VcsFullCommitDetails> selectedDetails = ContainerUtil.newArrayList();
for (int row : myUi.getTable().getSelectedRows()) {
VcsFullCommitDetails data = getDetailsAtRow(row);
if (data != null) {
selectedDetails.add(data);
@NotNull
public List<Hash> getSelectedCommits() {
List<Hash> hashes = ContainerUtil.newArrayList();
JBTable table = myUi.getTable();
for (int row : table.getSelectedRows()) {
CommitCell cell = (CommitCell)table.getModel().getValueAt(row, AbstractVcsLogTableModel.COMMIT_COLUMN);
Hash hash = cell.getHash();
if (hash != null) {
hashes.add(hash);
}
}
return selectedDetails;
return hashes;
}
@Override
@Nullable
public VcsFullCommitDetails getDetailsIfAvailable(@NotNull final Hash hash) {
return myDataHolder.getCommitDetailsGetter().getCommitDataIfAvailable(hash);
}
@Nullable
@@ -69,14 +78,4 @@ public class VcsLogImpl implements VcsLog {
return null;
}
@Nullable
private VcsFullCommitDetails getDetailsAtRow(int row) {
Node commitNode = myDataHolder.getDataPack().getGraphModel().getGraph().getCommitNodeInRow(row);
if (commitNode == null) {
return null;
}
VcsFullCommitDetails details = myDataHolder.getCommitDetailsGetter().getCommitData(commitNode);
return details instanceof LoadingDetails ? null : details;
}
}
@@ -23,7 +23,7 @@ import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsLog;
import org.jetbrains.annotations.Nullable;
@@ -42,15 +42,15 @@ public class VcsLogCopyHashAction extends DumbAwareAction {
if (log == null) {
return;
}
List<VcsFullCommitDetails> commits = log.getSelectedCommits();
List<Hash> commits = log.getSelectedCommits();
if (commits.isEmpty()) {
return;
}
String hashes = StringUtil.join(commits, new Function<VcsFullCommitDetails, String>() {
String hashes = StringUtil.join(commits, new Function<Hash, String>() {
@Override
public String fun(VcsFullCommitDetails details) {
return details.getHash().asString();
public String fun(Hash hash) {
return hash.asString();
}
}, "\n");
CopyPasteManager.getInstance().setContents(new StringSelection(hashes));
@@ -15,10 +15,11 @@ import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.graph.elements.Node;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import com.intellij.vcs.log.graph.render.CommitCell;
import com.intellij.vcs.log.graph.render.PrintParameters;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import com.intellij.vcs.log.ui.render.RefPainter;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -75,25 +76,21 @@ class DetailsPanel extends JPanel implements ListSelectionListener {
public void valueChanged(@Nullable ListSelectionEvent notUsed) {
int[] rows = myGraphTable.getSelectedRows();
if (rows.length < 1) {
myLoadingPanel.stopLoading();
((CardLayout)getLayout()).show(this, MESSAGE_LAYER);
myMessagePanel.setText("Nothing selected");
showMessage("Nothing selected");
}
else if (rows.length > 1) {
myLoadingPanel.stopLoading();
((CardLayout)getLayout()).show(this, MESSAGE_LAYER);
myMessagePanel.setText("Several commits selected");
showMessage("Several commits selected");
}
else {
((CardLayout)getLayout()).show(this, STANDARD_LAYER);
Node node = myLogDataHolder.getDataPack().getNode(rows[0]);
if (node == null) {
LOG.info("Couldn't find node for row " + rows[0] +
". All nodes: " + myLogDataHolder.getDataPack().getGraphModel().getGraph().getNodeRows());
CommitCell cell = (CommitCell)myGraphTable.getModel().getValueAt(rows[0], AbstractVcsLogTableModel.COMMIT_COLUMN);
Hash hash = cell.getHash();
if (hash == null) {
showMessage("Nothing selected");
return;
}
Hash hash = node.getCommitHash();
VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(node);
VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(hash);
if (commitData instanceof LoadingDetails) {
myLoadingPanel.startLoading();
myDataPanel.setData(null);
@@ -102,11 +99,17 @@ class DetailsPanel extends JPanel implements ListSelectionListener {
else {
myLoadingPanel.stopLoading();
myDataPanel.setData(commitData);
myRefsPanel.setRefs(sortRefs(hash, node.getBranch().getRepositoryRoot()));
myRefsPanel.setRefs(sortRefs(hash, commitData.getRoot()));
}
}
}
private void showMessage(String text) {
myLoadingPanel.stopLoading();
((CardLayout)getLayout()).show(this, MESSAGE_LAYER);
myMessagePanel.setText(text);
}
@NotNull
private List<VcsRef> sortRefs(@NotNull Hash hash, @NotNull VirtualFile root) {
Collection<VcsRef> refs = myLogDataHolder.getDataPack().getRefsModel().refsToCommit(hash);
@@ -5,6 +5,7 @@ import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.VcsShortCommitDetails;
@@ -114,11 +115,13 @@ public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell> {
GraphPrintCell graphPrintCell = myDataPack.getPrintCellModel().getGraphPrintCell(rowIndex);
String message = "";
List<VcsRef> refs = Collections.emptyList();
Hash hash = null;
if (details != null) {
hash = details.getHash();
message = details.getSubject();
refs = (List<VcsRef>)myDataPack.getRefsModel().refsToCommit(details.getHash());
}
return new GraphCommitCell(graphPrintCell, message, refs);
return new GraphCommitCell(hash, graphPrintCell, message, refs);
}
@NotNull
@@ -96,7 +96,7 @@ public class NoGraphTableModel extends AbstractVcsLogTableModel<CommitCell> {
subject = details.getSubject();
refs = myRefsModel.refsToCommit(details.getHash());
}
return new CommitCell(subject, refs);
return new CommitCell(myCommits.get(index).getHash(), subject, refs);
}
@NotNull
@@ -24,7 +24,6 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.Hash;
@@ -175,20 +174,27 @@ public class GitCherryPickAction extends DumbAwareAction {
if (project == null) {
return null;
}
VcsLog log = getVcsLog(project);
final VcsLog log = getVcsLog(project);
if (log == null) {
return null;
}
List<VcsFullCommitDetails> selectedCommits = log.getSelectedCommits();
// don't allow to cherry-pick if a non-Git commit was selected
// we could cherry-pick just Git commits filtered from the list, but it might provide confusion
boolean nonGitCommitSelected = ContainerUtil.find(selectedCommits, new Condition<VcsFullCommitDetails>() {
@Override
public boolean value(VcsFullCommitDetails details) {
return myPlatformFacade.getRepositoryManager(project).getRepositoryForRoot(details.getRoot()) == null;
List<Hash> selectedCommits = log.getSelectedCommits();
List<VcsFullCommitDetails> selectedDetails = ContainerUtil.newArrayList();
for (Hash commit : selectedCommits) {
VcsFullCommitDetails details = log.getDetailsIfAvailable(commit);
if (details == null) { // let the action be unavailable until all details are loaded
return null;
}
}) != null;
return nonGitCommitSelected ? null : selectedCommits;
GitRepository root = myPlatformFacade.getRepositoryManager(project).getRepositoryForRoot(details.getRoot());
// don't allow to cherry-pick if a non-Git commit was selected
// we could cherry-pick just Git commits filtered from the list, but it might provide confusion
if (root == null) {
return null;
}
selectedDetails.add(details);
}
return selectedDetails;
}
private static List<? extends VcsFullCommitDetails> convertHeavyCommitToFullDetails(List<GitHeavyCommit> commits) {
@@ -22,7 +22,11 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsLog;
import com.intellij.vcs.log.VcsLogObjectsFactory;
import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.impl.VcsLogImpl;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.history.browser.GitHeavyCommit;
@@ -80,9 +84,13 @@ public class GithubShowCommitInBrowserFromLogAction extends GithubShowCommitInBr
return factory.createShortDetails(factory.createHash(heavyCommit.getHash().getValue()), parents, heavyCommit.getAuthorTime(),
heavyCommit.getRoot(), heavyCommit.getSubject(), heavyCommit.getAuthor());
}
List<VcsFullCommitDetails> selectedCommits = ServiceManager.getService(e.getProject(), VcsLog.class).getSelectedCommits();
VcsLog log = ServiceManager.getService(e.getProject(), VcsLog.class);
if (log == null || !((VcsLogImpl)log).isReady()) {
return null;
}
List<Hash> selectedCommits = log.getSelectedCommits();
if (selectedCommits.size() == 1) {
return selectedCommits.get(0);
return log.getDetailsIfAvailable(selectedCommits.get(0));
}
return null;
}