From 410d2174ce002ea793f84eb6fdf1da22342bedb4 Mon Sep 17 00:00:00 2001 From: irengrig Date: Tue, 23 Feb 2010 21:59:16 +0300 Subject: [PATCH] VCS: lazy file history --- .../com/intellij/ui/dualView/DualView.java | 24 ++- .../src/com/intellij/ui/table/TableView.java | 2 +- .../intellij/util/BufferedListConsumer.java | 10 +- .../history/VcsAbstractHistorySession.java | 88 +++++++++ ...> VcsAppendableHistorySessionPartner.java} | 11 +- .../vcs/history/VcsHistoryProvider.java | 6 +- .../vcs/history/VcsHistorySession.java | 74 +------- .../CompareWithSelectedRevisionAction.java | 7 +- .../actions/SelectedBlockHistoryAction.java | 15 +- .../committed/CommittedChangesPanel.java | 2 +- .../vcs/history/FileHistoryPanelImpl.java | 167 ++++++++++-------- ...VcsHistoryProviderBackgroundableProxy.java | 46 ++++- .../vcs/impl/AbstractVcsHelperImpl.java | 119 ++++++++++--- .../history/CvsHistoryProvider.java | 33 ++-- .../src/git4idea/commands/GitLineHandler.java | 1 + .../git4idea/history/GitHistoryProvider.java | 48 ++++- .../src/git4idea/history/GitHistoryUtils.java | 102 +++++++++++ .../idea/svn/history/SvnHistoryProvider.java | 66 ++++--- 18 files changed, 601 insertions(+), 220 deletions(-) create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAbstractHistorySession.java rename platform/vcs-api/src/com/intellij/openapi/vcs/history/{FileHistoryPanel.java => VcsAppendableHistorySessionPartner.java} (64%) diff --git a/platform/platform-api/src/com/intellij/ui/dualView/DualView.java b/platform/platform-api/src/com/intellij/ui/dualView/DualView.java index 73c3bceb3ed3..4be8493975f5 100644 --- a/platform/platform-api/src/com/intellij/ui/dualView/DualView.java +++ b/platform/platform-api/src/com/intellij/ui/dualView/DualView.java @@ -24,17 +24,17 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.UIBundle; -import com.intellij.ui.treeStructure.Tree; import com.intellij.ui.table.BaseTableView; import com.intellij.ui.table.SelectionProvider; import com.intellij.ui.table.TableView; +import com.intellij.ui.treeStructure.Tree; +import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns; +import com.intellij.ui.treeStructure.treetable.TreeTableModel; import com.intellij.util.EditSourceOnDoubleClickHandler; import com.intellij.util.config.Storage; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.ListTableModel; import com.intellij.util.ui.Table; -import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns; -import com.intellij.ui.treeStructure.treetable.TreeTableModel; import org.jetbrains.annotations.NonNls; import javax.swing.*; @@ -392,8 +392,24 @@ public class DualView extends JPanel { BaseTableView.store(myTreeStorage, myTreeView); } - public void setRoot(TreeNode node) { + public void setRoot(final TreeNode node, final Object selection) { + ListTableModel model = myFlatView.getListTableModel(); + final int column = model.getSortedColumnIndex(); + final int sortingType = model.getSortingType(); + final Object obj = myFlatView.getSelectedObject() != null ? myFlatView.getSelectedObject() : selection; + myTreeView.getTreeViewModel().setRoot(node); + + if (column != -1) { + model.sortByColumn(column, sortingType); + } + if (obj != null) { + final List items = myFlatView.getItems(); + if (items.contains(obj)) { + final int idx = items.indexOf(obj); + setSelectionInterval(idx, idx); + } + } } public void rebuild() { diff --git a/platform/platform-api/src/com/intellij/ui/table/TableView.java b/platform/platform-api/src/com/intellij/ui/table/TableView.java index 8dc9f1d61ac9..c708a592a836 100644 --- a/platform/platform-api/src/com/intellij/ui/table/TableView.java +++ b/platform/platform-api/src/com/intellij/ui/table/TableView.java @@ -73,7 +73,7 @@ public class TableView extends BaseTableView implements ItemsProvider, Sel super.tableChanged(e); } - private void setSelection(Collection selection) { + public void setSelection(Collection selection) { clearSelection(); for (Iterator iterator = selection.iterator(); iterator.hasNext();) { addSelection(iterator.next()); diff --git a/platform/util/src/com/intellij/util/BufferedListConsumer.java b/platform/util/src/com/intellij/util/BufferedListConsumer.java index 78c05897c39c..2cb12a45a5f2 100644 --- a/platform/util/src/com/intellij/util/BufferedListConsumer.java +++ b/platform/util/src/com/intellij/util/BufferedListConsumer.java @@ -20,14 +20,18 @@ import java.util.Collections; import java.util.List; public class BufferedListConsumer implements Consumer> { + private final int myInterval; + private long myTs; private final int mySize; private final List myBuffer; private final Consumer> myConsumer; - public BufferedListConsumer(int size, Consumer> consumer) { + public BufferedListConsumer(int size, Consumer> consumer, int interval) { mySize = size; myBuffer = new ArrayList(); myConsumer = consumer; + myInterval = interval; + myTs = System.currentTimeMillis(); } public void consumeOne(final T t) { @@ -36,10 +40,12 @@ public class BufferedListConsumer implements Consumer> { public void consume(List list) { myBuffer.addAll(list); - if (mySize <= myBuffer.size()) { + final long ts = System.currentTimeMillis(); + if ((mySize <= myBuffer.size()) || (myInterval > 0) && ((ts - myInterval) > myTs)) { myConsumer.consume(new ArrayList(myBuffer)); myBuffer.clear(); } + myTs = ts; } public void flush() { diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAbstractHistorySession.java b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAbstractHistorySession.java new file mode 100644 index 000000000000..a708f67be11e --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAbstractHistorySession.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.history; + +import com.intellij.openapi.util.Comparing; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public abstract class VcsAbstractHistorySession implements VcsHistorySession { + private final List myRevisions; + private final Object myLock; + private VcsRevisionNumber myCachedRevisionNumber; + + protected VcsRevisionNumber getCachedRevision() { + synchronized (myLock) { + return myCachedRevisionNumber; + } + } + + protected void setCachedRevision(final VcsRevisionNumber number) { + synchronized (myLock) { + myCachedRevisionNumber = number; + } + } + + public VcsAbstractHistorySession(List revisions) { + myLock = new Object(); + myRevisions = new ArrayList(revisions); + myCachedRevisionNumber = calcCurrentRevisionNumber(); + } + + protected VcsAbstractHistorySession(List revisions, VcsRevisionNumber currentRevisionNumber) { + myLock = new Object(); + myRevisions = new ArrayList(revisions); + myCachedRevisionNumber = currentRevisionNumber; + } + + public List getRevisionList() { + return myRevisions; + } + + /** + * This method should return actual value for current revision (it can be changed after submit for example) + * @return current file revision, null if file does not exist anymore + */ + + @Nullable + protected abstract VcsRevisionNumber calcCurrentRevisionNumber(); + + public final VcsRevisionNumber getCurrentRevisionNumber() { + return getCachedRevision(); + } + + public boolean isCurrentRevision(VcsRevisionNumber rev) { + VcsRevisionNumber revNumber = getCurrentRevisionNumber(); + return revNumber != null && revNumber.compareTo(rev) == 0; + } + + public synchronized boolean shouldBeRefreshed() { + final VcsRevisionNumber oldValue = getCachedRevision(); + final VcsRevisionNumber newNumber = calcCurrentRevisionNumber(); + setCachedRevision(newNumber); + return !Comparing.equal(oldValue, newNumber); + } + + public boolean allowAsyncRefresh() { + return false; + } + + public boolean isContentAvailable(VcsFileRevision revision) { + return true; + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/history/FileHistoryPanel.java b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAppendableHistorySessionPartner.java similarity index 64% rename from platform/vcs-api/src/com/intellij/openapi/vcs/history/FileHistoryPanel.java rename to platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAppendableHistorySessionPartner.java index 6b7db30b461c..c81941e96684 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/history/FileHistoryPanel.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsAppendableHistorySessionPartner.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,11 @@ */ package com.intellij.openapi.vcs.history; -public interface FileHistoryPanel { - void refresh(); +import com.intellij.openapi.vcs.VcsException; + +public interface VcsAppendableHistorySessionPartner { + void reportCreatedEmptySession(VcsAbstractHistorySession session); + void acceptRevision(final VcsFileRevision revision); + void reportException(final VcsException exception); + void finished(); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistoryProvider.java b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistoryProvider.java index 8655c2e0b40b..5022363b310d 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistoryProvider.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistoryProvider.java @@ -29,7 +29,7 @@ public interface VcsHistoryProvider extends VcsProviderMarker { VcsDependentHistoryComponents getUICustomization(final VcsHistorySession session, final JComponent forShortcutRegistration); - AnAction[] getAdditionalActions(final FileHistoryPanel panel); + AnAction[] getAdditionalActions(final Runnable refresher); /** * Returns whether the history provider submits the custom-formatted date @@ -53,9 +53,7 @@ public interface VcsHistoryProvider extends VcsProviderMarker { @CalledInBackground VcsHistorySession createSessionFor(FilePath filePath) throws VcsException; - //return null if your revisions cannot be tree - @Nullable - HistoryAsTreeProvider getTreeHistoryProvider(); + void reportAppendableHistory(final FilePath path, final VcsAppendableHistorySessionPartner partner) throws VcsException; boolean supportsHistoryForDirectories(); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistorySession.java b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistorySession.java index abae1d52599d..40129efd47f0 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistorySession.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/history/VcsHistorySession.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,73 +15,17 @@ */ package com.intellij.openapi.vcs.history; -import com.intellij.openapi.util.Comparing; import org.jetbrains.annotations.Nullable; import java.util.List; -public abstract class VcsHistorySession { - private final List myRevisions; - private final Object myLock; - private VcsRevisionNumber myCachedRevisionNumber; - - protected VcsRevisionNumber getCachedRevision() { - synchronized (myLock) { - return myCachedRevisionNumber; - } - } - - protected void setCachedRevision(final VcsRevisionNumber number) { - synchronized (myLock) { - myCachedRevisionNumber = number; - } - } - - public VcsHistorySession(List revisions) { - myLock = new Object(); - myRevisions = revisions; - myCachedRevisionNumber = calcCurrentRevisionNumber(); - } - - protected VcsHistorySession(List revisions, VcsRevisionNumber currentRevisionNumber) { - myLock = new Object(); - myRevisions = revisions; - myCachedRevisionNumber = currentRevisionNumber; - } - - public List getRevisionList() { - return myRevisions; - } - - /** - * This method should return actual value for current revision (it can be changed after submit for example) - * @return current file revision, null if file does not exist anymore - */ - +public interface VcsHistorySession { + List getRevisionList(); + VcsRevisionNumber getCurrentRevisionNumber(); + boolean isCurrentRevision(VcsRevisionNumber rev); + boolean shouldBeRefreshed(); + boolean allowAsyncRefresh(); + boolean isContentAvailable(VcsFileRevision revision); @Nullable - protected abstract VcsRevisionNumber calcCurrentRevisionNumber(); - - public final VcsRevisionNumber getCurrentRevisionNumber() { - return getCachedRevision(); - } - - public boolean isCurrentRevision(VcsRevisionNumber rev) { - VcsRevisionNumber revNumber = getCurrentRevisionNumber(); - return revNumber != null && revNumber.compareTo(rev) == 0; - } - - public synchronized boolean refresh() { - final VcsRevisionNumber oldValue = getCachedRevision(); - final VcsRevisionNumber newNumber = calcCurrentRevisionNumber(); - setCachedRevision(newNumber); - return !Comparing.equal(oldValue, newNumber); - } - - public boolean allowAsyncRefresh() { - return false; - } - - public boolean isContentAvailable(VcsFileRevision revision) { - return true; - } + HistoryAsTreeProvider getHistoryAsTreeProvider(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CompareWithSelectedRevisionAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CompareWithSelectedRevisionAction.java index bb1e74660d1d..bedd65ad47b5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CompareWithSelectedRevisionAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CompareWithSelectedRevisionAction.java @@ -20,7 +20,10 @@ import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.PopupChooserBuilder; -import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.diff.DiffProvider; import com.intellij.openapi.vcs.history.*; import com.intellij.openapi.vcs.impl.VcsBackgroundableActions; @@ -115,7 +118,7 @@ public class CompareWithSelectedRevisionAction extends AbstractVcsAction { public void consume(VcsHistorySession session) { if (session == null) return; final List revisions = session.getRevisionList(); - final HistoryAsTreeProvider treeHistoryProvider = vcsHistoryProvider.getTreeHistoryProvider(); + final HistoryAsTreeProvider treeHistoryProvider = session.getHistoryAsTreeProvider(); if (treeHistoryProvider != null) { showTreePopup(treeHistoryProvider.createTreeOn(revisions), file, project, vcs.getDiffProvider()); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java index 79d025a4d5ed..788fc455c0bb 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java @@ -20,18 +20,21 @@ import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.vcs.*; -import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; -import com.intellij.openapi.vcs.impl.BackgroundableActionEnabledHandler; -import com.intellij.openapi.vcs.impl.VcsBackgroundableActions; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.history.VcsHistoryProvider; -import com.intellij.openapi.vcs.history.VcsHistorySession; import com.intellij.openapi.vcs.history.VcsHistoryProviderBackgroundableProxy; +import com.intellij.openapi.vcs.history.VcsHistorySession; import com.intellij.openapi.vcs.history.impl.VcsBlockHistoryDialog; +import com.intellij.openapi.vcs.impl.BackgroundableActionEnabledHandler; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vcs.impl.VcsBackgroundableActions; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; import com.intellij.vcsUtil.VcsSelection; import com.intellij.vcsUtil.VcsSelectionUtil; -import com.intellij.util.Consumer; public class SelectedBlockHistoryAction extends AbstractVcsAction { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesPanel.java index 57f3aa19d79c..9dbf77a5f28f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesPanel.java @@ -152,7 +152,7 @@ public class CommittedChangesPanel extends JPanel implements TypeSafeDataProvide }.callMe(); } }; - final BufferedListConsumer bufferedListConsumer = new BufferedListConsumer(30, appender); + final BufferedListConsumer bufferedListConsumer = new BufferedListConsumer(30, appender,-1); myProvider.loadCommittedChanges(mySettings, myLocation, myMaxCount, new AsynchConsumer() { public void finished() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index 89801783d782..61354f08818e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -18,8 +18,8 @@ package com.intellij.openapi.vcs.history; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.*; @@ -45,6 +45,7 @@ import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.actions.CreatePatchFromChangesAction; +import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater; import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer; import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer; import com.intellij.openapi.vcs.changes.issueLinks.TableLinkMouseListener; @@ -67,10 +68,7 @@ import com.intellij.ui.dualView.CellWrapper; import com.intellij.ui.dualView.DualTreeElement; import com.intellij.ui.dualView.DualView; import com.intellij.ui.dualView.DualViewColumnInfo; -import com.intellij.util.Alarm; -import com.intellij.util.Consumer; -import com.intellij.util.Icons; -import com.intellij.util.TreeItem; +import com.intellij.util.*; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.SortableColumnModel; import com.intellij.util.ui.TableViewModel; @@ -106,7 +104,7 @@ import java.util.List; /** * author: lesya */ -public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton implements FileHistoryPanel { +public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { private static final Logger LOG = Logger.getInstance("#com.intellij.cvsSupport2.ui.FileHistoryDialog"); private final JEditorPane myComments; @@ -119,14 +117,18 @@ public class FileHistoryPanelImpl myCommittedChangesProvider; private final FilePath myFilePath; + private final Runnable myRefresher; private final DualView myDualView; private final Alarm myUpdateAlarm; private final String myRepositoryPath; + private boolean myInRefresh; + private Object myTargetSelection; + private final AsynchConsumer myHistoryPanelRefresh; + private static final String COMMIT_MESSAGE_TITLE = VcsBundle.message("label.selected.revision.commit.message"); @NonNls private static final String VCS_HISTORY_ACTIONS_GROUP = "VcsHistoryActionsGroup"; @@ -174,6 +176,8 @@ public class FileHistoryPanelImpl committedChangesProvider) { + ContentManager contentManager, final Runnable refresher) { super(contentManager, provider.getHelpId() != null ? provider.getHelpId() : "reference.versionControl.toolwindow.history"); myProvider = provider; myAnnotationProvider = annotationProvider; - myCommittedChangesProvider = committedChangesProvider; myRepositoryPath = repositoryPath; myProject = project; - myHistorySession = session; + myRefresher = refresher; + myHistorySession = session; myFilePath = filePath; COLUMNS = createColumnList(project, provider, session); @@ -277,15 +281,15 @@ public class FileHistoryPanelImpl(myHistorySession.getRevisionList()))), COLUMNS, storageKey, project); } else { - myDualView = new DualView(new TreeNodeOnVcsRevision(null, wrapWithTreeElements(myHistorySession.getRevisionList())), COLUMNS, + myDualView = new DualView(new TreeNodeOnVcsRevision(null, wrapWithTreeElements(new ArrayList(myHistorySession.getRevisionList()))), COLUMNS, storageKey, project); myDualView.switchToTheFlatMode(); } @@ -297,33 +301,31 @@ public class FileHistoryPanelImpl() { + public void finished() { + myInRefresh = false; + myTargetSelection = null; + + myLoadingLabel.setVisible(false); + mySplitter.revalidate(); + mySplitter.repaint(); + } + public void consume(VcsHistorySession vcsHistorySession) { + FileHistoryPanelImpl.this.refresh(vcsHistorySession); + } + }; + myUpdateAlarm.addRequest(new Runnable() { public void run() { if (myProject.isDisposed()) { return; } - final boolean refresh = myHistorySession.refresh(); + final boolean refresh = (! myInRefresh) && myHistorySession.shouldBeRefreshed(); myUpdateAlarm.cancelAllRequests(); myUpdateAlarm.addRequest(this, 10000); if (refresh) { - createSession(new Consumer() { - public void consume(final VcsHistorySession session) { - if (session != null) { - if (session.allowAsyncRefresh()) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - refresh(session); - } - }); - } - else { - refresh(session); - } - } - } - }); - + refreshImpl(); } } }, 10000); @@ -333,20 +335,6 @@ public class FileHistoryPanelImpl consumer) { - final Runnable runnable = new Runnable() { - public void run() { - new VcsHistoryProviderBackgroundableProxy(myProject, getHistoryProvider()).createSessionFor(myFilePath, consumer, null, true); - } - }; - final Application application = ApplicationManager.getApplication(); - if (application.isDispatchThread()) { - runnable.run(); - } else { - application.invokeLater(runnable); - } - } - private void replaceTransferable() { final TransferHandler originalTransferHandler = myComments.getTransferHandler(); @@ -437,18 +425,19 @@ public class FileHistoryPanelImpl(myHistorySession.getRevisionList()))), myTargetSelection); } else { - myDualView.setRoot(new TreeNodeOnVcsRevision(null, wrapWithTreeElements(myHistorySession.getRevisionList()))); + myDualView.setRoot(new TreeNodeOnVcsRevision(null, + wrapWithTreeElements(new ArrayList(myHistorySession.getRevisionList()))), myTargetSelection); } - myDualView.rebuild(); myDualView.expandAll(); myDualView.repaint(); } @@ -626,11 +615,11 @@ public class FileHistoryPanelImpl() { - public void consume(VcsHistorySession session) { - if (session == null) return; - refresh(session); + private void refreshImpl() { + new AbstractCalledLater(myProject, ModalityState.NON_MODAL) { + public void run() { + if (myInRefresh) return; + myInRefresh = true; + myTargetSelection = myDualView.getFlatView().getSelectedObject(); + + myLoadingLabel.setVisible(true); + mySplitter.revalidate(); + mySplitter.repaint(); + + myRefresher.run(); } - }); + }.callMe(); + } + + public AsynchConsumer getHistoryPanelRefresh() { + return myHistoryPanelRefresh; } private boolean supportsTree() { - return getHistoryProvider().getTreeHistoryProvider() != null; - } - - private VcsHistoryProvider getHistoryProvider() { - return myProvider; + return myHistorySession != null && myHistorySession.getHistoryAsTreeProvider() != null; } private class MyShowAsTreeAction extends ToggleAction implements DumbAware { @@ -1274,6 +1281,22 @@ public class FileHistoryPanelImpl() { - public void consume(VcsHistorySession session) { - if (session == null) return; - List revisionsList = session.getRevisionList(); - if (revisionsList.isEmpty()) return; + private static class MyVcsAppendableHistorySessionPartner implements VcsAppendableHistorySessionPartner { + private FileHistoryPanelImpl myFileHistoryPanel; + private final VcsHistoryProvider myVcsHistoryProvider; + private final AnnotationProvider myAnnotationProvider; + private final FilePath myPath; + private final String myRepositoryPath; + private final AbstractVcs myVcs; + private final Runnable myRefresher; + private VcsAbstractHistorySession mySession; + private BufferedListConsumer myBuffer; - String actionName = VcsBundle.message("action.name.file.history", path.getName()); + private MyVcsAppendableHistorySessionPartner(final VcsHistoryProvider vcsHistoryProvider, final AnnotationProvider annotationProvider, + final FilePath path, + final String repositoryPath, + final AbstractVcs vcs, + final Runnable refresher) { + myVcsHistoryProvider = vcsHistoryProvider; + myAnnotationProvider = annotationProvider; + myPath = path; + myRepositoryPath = repositoryPath; + myVcs = vcs; + myRefresher = refresher; + myBuffer = new BufferedListConsumer(5, new Consumer>() { + public void consume(List vcsFileRevisions) { + mySession.getRevisionList().addAll(vcsFileRevisions); + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + myFileHistoryPanel.getHistoryPanelRefresh().consume(mySession); + } + }); + } + }, 1000); + } - ContentManager contentManager = ProjectLevelVcsManagerEx.getInstanceEx(myProject).getContentManager(); + public void acceptRevision(VcsFileRevision revision) { + myBuffer.consumeOne(revision); + } - FileHistoryPanelImpl fileHistoryPanel = - new FileHistoryPanelImpl(myProject, path, repositoryPath, session, vcsHistoryProvider, annotationProvider, contentManager, - vcs.getCommittedChangesProvider()); - Content content = ContentFactory.SERVICE.getInstance().createContent(fileHistoryPanel, actionName, true); + public void reportCreatedEmptySession(final VcsAbstractHistorySession session) { + mySession = session; + if (myFileHistoryPanel != null) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + myFileHistoryPanel.getHistoryPanelRefresh().consume(mySession); + } + }); + return; + } + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + String actionName = VcsBundle.message("action.name.file.history", myPath.getName()); + ContentManager contentManager = ProjectLevelVcsManagerEx.getInstanceEx(myVcs.getProject()).getContentManager(); + + myFileHistoryPanel = new FileHistoryPanelImpl(myVcs.getProject(), myPath, myRepositoryPath, session, myVcsHistoryProvider, + myAnnotationProvider, contentManager, myRefresher); + Content content = ContentFactory.SERVICE.getInstance().createContent(myFileHistoryPanel, actionName, true); ContentsUtil.addOrReplaceContent(contentManager, content, true); - ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.VCS); + ToolWindow toolWindow = ToolWindowManager.getInstance(myVcs.getProject()).getToolWindow(ToolWindowId.VCS); toolWindow.activate(null); } - }, null, false); - } - catch (Exception exception) { - reportError(exception); + }); } + public void reportException(VcsException exception) { + ChangesViewBalloonProblemNotifier.showMe(myVcs.getProject(), VcsBundle.message("message.title.could.not.load.file.history") + ": " + + exception.getMessage(), MessageType.ERROR); + } + + public void finished() { + myBuffer.flush(); + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + if (myFileHistoryPanel != null) { + myFileHistoryPanel.getHistoryPanelRefresh().finished(); + } + } + }); + } + } + + private static class MyRefresher implements Runnable { + private MyVcsAppendableHistorySessionPartner mySessionPartner; + private final VcsHistoryProvider myVcsHistoryProvider; + private final FilePath myPath; + private final AbstractVcs myVcs; + + private MyRefresher(final VcsHistoryProvider vcsHistoryProvider, final AnnotationProvider annotationProvider, final FilePath path, + final String repositoryPath, final AbstractVcs vcs) { + myVcsHistoryProvider = vcsHistoryProvider; + myPath = path; + myVcs = vcs; + mySessionPartner = new MyVcsAppendableHistorySessionPartner(vcsHistoryProvider, annotationProvider, path, repositoryPath, vcs, this); + } + + public void run() { + final VcsHistoryProviderBackgroundableProxy proxy = new VcsHistoryProviderBackgroundableProxy(myVcs.getProject(), myVcsHistoryProvider); + proxy.executeAppendableSession(myPath, mySessionPartner, null, false); + } + } + + public void showFileHistory(final VcsHistoryProvider vcsHistoryProvider, final AnnotationProvider annotationProvider, final FilePath path, + final String repositoryPath, final AbstractVcs vcs) { + final MyRefresher refresher = new MyRefresher(vcsHistoryProvider, annotationProvider, path, repositoryPath, vcs); + refresher.run(); } public void showRollbackChangesDialog(List changes) { @@ -499,7 +577,6 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper { } } else { - // todo openCommittedChangesTab(provider, location, settings, 0, title); } } @@ -626,7 +703,7 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper { @Override public void run(@NotNull final ProgressIndicator indicator) { final AsynchConsumer> appender = myDlg.getAppender(); - final BufferedListConsumer bufferedListConsumer = new BufferedListConsumer(10, appender); + final BufferedListConsumer bufferedListConsumer = new BufferedListConsumer(10, appender, -1); final Application application = ApplicationManager.getApplication(); try { diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/history/CvsHistoryProvider.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/history/CvsHistoryProvider.java index cc45826a6062..4334d852f032 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/history/CvsHistoryProvider.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/history/CvsHistoryProvider.java @@ -17,9 +17,8 @@ package com.intellij.cvsSupport2.history; import com.intellij.CvsBundle; import com.intellij.cvsSupport2.CvsUtil; -import com.intellij.cvsSupport2.util.CvsVfsUtil; -import com.intellij.cvsSupport2.changeBrowser.CvsChangeList; import com.intellij.cvsSupport2.application.CvsEntriesManager; +import com.intellij.cvsSupport2.changeBrowser.CvsChangeList; import com.intellij.cvsSupport2.connections.CvsConnectionSettings; import com.intellij.cvsSupport2.connections.CvsEnvironment; import com.intellij.cvsSupport2.cvsExecution.CvsOperationExecutor; @@ -28,9 +27,11 @@ import com.intellij.cvsSupport2.cvsExecution.ModalityContext; import com.intellij.cvsSupport2.cvshandlers.CommandCvsHandler; import com.intellij.cvsSupport2.cvsoperations.cvsLog.LocalPathIndifferentLogOperation; import com.intellij.cvsSupport2.cvsoperations.cvsTagOrBranch.ui.TagsPanel; +import com.intellij.cvsSupport2.util.CvsVfsUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.history.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.TreeItem; @@ -157,19 +158,19 @@ public class CvsHistoryProvider implements VcsHistoryProvider { public VcsHistorySession createSessionFor(final FilePath filePath) { final List fileRevisionList = createRevisions(filePath); if (fileRevisionList == null) return null; - return new VcsHistorySession(fileRevisionList) { + return new VcsAbstractHistorySession(fileRevisionList) { @Nullable public VcsRevisionNumber calcCurrentRevisionNumber() { return getCurrentRevision(filePath); } @Override - public synchronized boolean refresh() { + public synchronized boolean shouldBeRefreshed() { //noinspection SimplifiableIfStatement if (!CvsEntriesManager.getInstance().isActive()) { return false; } - return super.refresh(); + return super.shouldBeRefreshed(); } public boolean isContentAvailable(final VcsFileRevision revision) { @@ -179,9 +180,19 @@ public class CvsHistoryProvider implements VcsHistoryProvider { } return super.isContentAvailable(revision); } + + public HistoryAsTreeProvider getHistoryAsTreeProvider() { + return MyHistoryAsTreeProvider.getInstance(); + } }; } + public void reportAppendableHistory(FilePath path, VcsAppendableHistorySessionPartner partner) throws VcsException { + // todo some time after ... this could be done + final VcsHistorySession session = createSessionFor(path); + partner.reportCreatedEmptySession((VcsAbstractHistorySession) session); + } + private static VcsRevisionNumber getCurrentRevision(FilePath filePath) { Entry entryFor = CvsEntriesManager.getInstance().getEntryFor(filePath.getVirtualFileParent(), filePath.getName()); @@ -238,19 +249,21 @@ public class CvsHistoryProvider implements VcsHistoryProvider { return result; } - public AnAction[] getAdditionalActions(final FileHistoryPanel panel) { + public AnAction[] getAdditionalActions(final Runnable refresher) { return AnAction.EMPTY_ARRAY; } - public HistoryAsTreeProvider getTreeHistoryProvider() { - return new MyHistoryAsTreeProvider(); - } - public boolean supportsHistoryForDirectories() { return false; } private static class MyHistoryAsTreeProvider implements HistoryAsTreeProvider { + private static MyHistoryAsTreeProvider ourInstance = new MyHistoryAsTreeProvider(); + + public static MyHistoryAsTreeProvider getInstance() { + return ourInstance; + } + public List> createTreeOn(List allRevisions) { List sortedRevisions = sortRevisions(allRevisions); diff --git a/plugins/git4idea/src/git4idea/commands/GitLineHandler.java b/plugins/git4idea/src/git4idea/commands/GitLineHandler.java index f6b11f2be466..5c670e27daf9 100644 --- a/plugins/git4idea/src/git4idea/commands/GitLineHandler.java +++ b/plugins/git4idea/src/git4idea/commands/GitLineHandler.java @@ -112,6 +112,7 @@ public class GitLineHandler extends GitHandler { * @param lineBuilder a line builder */ private void notifyLines(final Key outputType, final Iterator lines, final StringBuilder lineBuilder) { + if (! lines.hasNext()) return; if (lineBuilder.length() > 0) { lineBuilder.append(lines.next()); if (lines.hasNext()) { diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java index 7e3a2ae519e9..71659451f06f 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java @@ -21,12 +21,15 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.history.*; +import com.intellij.util.Consumer; import com.intellij.util.ui.ColumnInfo; +import git4idea.GitFileRevision; import git4idea.actions.GitShowAllSubmittedFilesAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Collections; import java.util.List; /** @@ -60,8 +63,9 @@ public class GitHistoryProvider implements VcsHistoryProvider { /** * {@inheritDoc} + * @param refresher */ - public AnAction[] getAdditionalActions(FileHistoryPanel panel) { + public AnAction[] getAdditionalActions(Runnable refresher) { return new AnAction[]{new GitShowAllSubmittedFilesAction(), new GitCopyHistoryRevisionNumberAction()}; } @@ -89,7 +93,7 @@ public class GitHistoryProvider implements VcsHistoryProvider { return null; } List revisions = GitHistoryUtils.history(project, filePath); - return new VcsHistorySession(revisions) { + return new VcsAbstractHistorySession(revisions) { @Nullable protected VcsRevisionNumber calcCurrentRevisionNumber() { try { @@ -103,15 +107,43 @@ public class GitHistoryProvider implements VcsHistoryProvider { return null; } } + + public HistoryAsTreeProvider getHistoryAsTreeProvider() { + return null; + } }; } - /** - * {@inheritDoc} - */ - @Nullable - public HistoryAsTreeProvider getTreeHistoryProvider() { - return null; + public void reportAppendableHistory(final FilePath path, final VcsAppendableHistorySessionPartner partner) throws VcsException { + final VcsAbstractHistorySession emptySession = new VcsAbstractHistorySession(Collections.emptyList()) { + @Nullable + protected VcsRevisionNumber calcCurrentRevisionNumber() { + try { + return GitHistoryUtils.getCurrentRevision(project, GitHistoryUtils.getLastCommitName(project, path)); + } + catch (VcsException e) { + // likely the file is not under VCS anymore. + if (log.isDebugEnabled()) { + log.debug("Unable to retrieve the current revision number", e); + } + return null; + } + } + + public HistoryAsTreeProvider getHistoryAsTreeProvider() { + return null; + } + }; + partner.reportCreatedEmptySession(emptySession); + GitHistoryUtils.history(project, path, new Consumer() { + public void consume(GitFileRevision gitFileRevision) { + partner.acceptRevision(gitFileRevision); + } + }, new Consumer() { + public void consume(VcsException e) { + partner.reportException(e); + } + }); } /** diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java index ab229b64f459..ca6449eaf18f 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java @@ -17,6 +17,7 @@ package git4idea.history; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; @@ -25,10 +26,14 @@ import com.intellij.openapi.vcs.diff.ItemLatestState; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; +import com.intellij.util.concurrency.Semaphore; import com.intellij.util.text.StringTokenizer; import com.intellij.vcsUtil.VcsUtil; import git4idea.*; import git4idea.commands.GitCommand; +import git4idea.commands.GitLineHandler; +import git4idea.commands.GitLineHandlerAdapter; import git4idea.commands.GitSimpleHandler; import org.jetbrains.annotations.Nullable; @@ -115,6 +120,103 @@ public class GitHistoryUtils { return new ItemLatestState(new GitRevisionNumber(hash, commitDate), lines[2].charAt(0) != 'D', false); } + public static void history(final Project project, FilePath path, final Consumer consumer, + final Consumer exceptionConsumer) throws VcsException { + // adjust path using change manager + path = getLastCommitName(project, path); + final VirtualFile root = GitUtil.getGitRoot(path); + GitLineHandler h = new GitLineHandler(project, root, GitCommand.LOG); + h.setNoSSH(true); + h.setStdoutSuppressed(true); + h.addParameters("-M", "--follow", "--name-only", + "--pretty=format:%H%x00%ct%x00%an%x20%x3C%ae%x3E%x00%cn%x20%x3C%ce%x3E%x00%s%n%n%b%x00", "--encoding=UTF-8"); + h.endOptions(); + h.addRelativePaths(path); + + final String prefix = root.getPath() + "/"; + final MyTokenAccomulator accomulator = new MyTokenAccomulator(6); + + final Consumer> resultAdapter = new Consumer>() { + public void consume(List result) { + final GitRevisionNumber revision = new GitRevisionNumber(result.get(0), GitUtil.parseTimestamp(result.get(1))); + final String author = GitUtil.adjustAuthorName(result.get(2), result.get(3)); + final String message = result.get(4).trim(); + + String path = ""; + try { + path = GitUtil.unescapePath(result.get(5)); + } + catch (VcsException e) { + exceptionConsumer.consume(e); + } + final FilePath revisionPath = VcsUtil.getFilePathForDeletedFile(prefix + path, false); + consumer.consume(new GitFileRevision(project, revisionPath, revision, author, message, null)); + } + }; + + final Semaphore semaphore = new Semaphore(); + h.addLineListener(new GitLineHandlerAdapter() { + @Override + public void onLineAvailable(String line, Key outputType) { + final List result = accomulator.acceptLine(line); + if (result != null) { + resultAdapter.consume(result); + } + } + @Override + public void startFailed(Throwable exception) { + exceptionConsumer.consume(new VcsException(exception)); + } + + @Override + public void processTerminated(int exitCode) { + super.processTerminated(exitCode); + semaphore.up(); + } + }); + semaphore.down(); + h.start(); + semaphore.waitFor(); + } + + private static class MyTokenAccomulator { + private int myCnt; + private List mySb; + private final int myMax; + + private MyTokenAccomulator(final int max) { + myMax = max; + mySb = new ArrayList(6); + myCnt = 0; + } + + @Nullable + public List acceptLine(final String s) { + StringTokenizer tk = new StringTokenizer(s.trim(), "\u0000", false); + List result = null; + while (tk.hasMoreElements()) { + final String token = tk.nextToken(); + final List curResult = acceptPieces(token); + if (curResult != null) { + result = curResult; + } + } + return result; + } + + @Nullable + private List acceptPieces(final String s) { + mySb.add(s); + ++ myCnt; + if (myMax == myCnt) { + myCnt = 0; + final List result = mySb; + mySb = new ArrayList(6); + return result; + } + return null; + } + } /** * Get history for the file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java index 73430fe5e331..e82d726bc961 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -33,6 +33,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.CollectConsumer; import com.intellij.util.Consumer; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.UIUtil; @@ -75,10 +76,6 @@ public class SvnHistoryProvider implements VcsHistoryProvider { myDirectory = isDirectory; } - public HistoryAsTreeProvider getTreeHistoryProvider() { - return null; - } - public boolean supportsHistoryForDirectories() { return true; } @@ -133,7 +130,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider { return new VcsDependentHistoryComponents(columns, listener, addComp); } - private class MyHistorySession extends VcsHistorySession { + private class MyHistorySession extends VcsAbstractHistorySession { private final FilePath myCommittedPath; private final Map myListsMap; private final boolean mySupports15; @@ -143,13 +140,17 @@ public class SvnHistoryProvider implements VcsHistoryProvider { myCommittedPath = committedPath; mySupports15 = supports15; myListsMap = new HashMap(); - refresh(); + shouldBeRefreshed(); } public Map getListsMap() { return myListsMap; } + public HistoryAsTreeProvider getHistoryAsTreeProvider() { + return null; + } + @Nullable public VcsRevisionNumber calcCurrentRevisionNumber() { if (myCommittedPath == null) { @@ -176,17 +177,35 @@ public class SvnHistoryProvider implements VcsHistoryProvider { public VcsHistorySession createSessionFor(final FilePath filePath) throws VcsException { final FilePath committedPath = ChangesUtil.getCommittedPath(myVcs.getProject(), filePath); final Ref supports15Ref = new Ref(); - final List revisions = getRevisionsList(committedPath, supports15Ref); - if (revisions == null) { - return null; - } + final List revisions = new ArrayList(); + getRevisionsList(committedPath, supports15Ref, new CollectConsumer(revisions)); return new MyHistorySession(revisions, committedPath, Boolean.TRUE.equals(supports15Ref.get())); } + public void reportAppendableHistory(FilePath path, final VcsAppendableHistorySessionPartner partner) throws VcsException { + final FilePath committedPath = ChangesUtil.getCommittedPath(myVcs.getProject(), path); + final Ref supports15Ref = new Ref(); + + final MyHistorySession historySession = + new MyHistorySession(Collections.emptyList(), committedPath, Boolean.TRUE.equals(supports15Ref.get())); + + final Ref sessionReported = new Ref(); + + getRevisionsList(committedPath, supports15Ref, new Consumer() { + public void consume(VcsFileRevision vcsFileRevision) { + if (! Boolean.TRUE.equals(sessionReported.get())) { + partner.reportCreatedEmptySession(historySession); + sessionReported.set(true); + } + partner.acceptRevision(vcsFileRevision); + } + }); + } + @Nullable - private List getRevisionsList(final FilePath file, final Ref supports15Ref) throws VcsException { + private void getRevisionsList(final FilePath file, final Ref supports15Ref, + final Consumer consumer) throws VcsException { final SVNException[] exception = new SVNException[1]; - final ArrayList result = new ArrayList(); Runnable command = new Runnable() { public void run() { @@ -196,9 +215,9 @@ public class SvnHistoryProvider implements VcsHistoryProvider { } try { if (myURL == null) { - collectLogEntries(indicator, file, exception, result, supports15Ref); + collectLogEntries(indicator, file, exception, consumer, supports15Ref); } else { - collectLogEntriesForRepository(indicator, result, supports15Ref); + collectLogEntriesForRepository(indicator, consumer, supports15Ref); } } catch(SVNCancelException ex) { @@ -215,11 +234,10 @@ public class SvnHistoryProvider implements VcsHistoryProvider { if (exception[0] != null) { throw new VcsException(exception[0]); } - return result; } private void collectLogEntries(final ProgressIndicator indicator, FilePath file, SVNException[] exception, - final ArrayList result, final Ref supports15Ref) throws SVNException { + final Consumer result, final Ref supports15Ref) throws SVNException { SVNWCClient wcClient = myVcs.createWCClient(); SVNInfo info = wcClient.doInfo(new File(file.getIOFile().getAbsolutePath()), SVNRevision.WORKING); wcClient.setEventHandler(new ISVNEventHandler() { @@ -255,7 +273,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider { new MyLogEntryHandler(url, pegRevision, relativeUrl, result)); } - private void collectLogEntriesForRepository(final ProgressIndicator indicator, final ArrayList result, + private void collectLogEntriesForRepository(final ProgressIndicator indicator, final Consumer result, final Ref supports15Ref) throws SVNException { if (indicator != null) { indicator.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", myURL.toString())); @@ -299,7 +317,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider { } } - public AnAction[] getAdditionalActions(final FileHistoryPanel panel) { + public AnAction[] getAdditionalActions(final Runnable refresher) { return new AnAction[]{new ShowAllSubmittedFilesAction(), new MergeSourceDetailsAction()}; } @@ -310,12 +328,13 @@ public class SvnHistoryProvider implements VcsHistoryProvider { private class MyLogEntryHandler implements ISVNLogEntryHandler { private final ProgressIndicator myIndicator; private String myLastPath; - protected final ArrayList myResult; + protected final Consumer myResult; + private VcsFileRevision myPrevious; private final SVNRevision myPegRevision; private final String myUrl; private int myMergeLevel; - public MyLogEntryHandler(final String url, final SVNRevision pegRevision, String lastPath, final ArrayList result) { + public MyLogEntryHandler(final String url, final SVNRevision pegRevision, String lastPath, final Consumer result) { myLastPath = lastPath; myIndicator = ProgressManager.getInstance().getProgressIndicator(); myResult = result; @@ -361,9 +380,10 @@ public class SvnHistoryProvider implements VcsHistoryProvider { } final SvnFileRevision revision = createRevision(logEntry, copyPath); if (myMergeLevel >= 0) { - addToListByLevel((SvnFileRevision) myResult.get(myResult.size() - 1), revision, myMergeLevel); + addToListByLevel((SvnFileRevision) myPrevious, revision, myMergeLevel); } else { - myResult.add(revision); + myResult.consume(revision); + myPrevious = revision; } if (logEntry.hasChildren()) { ++ myMergeLevel; @@ -394,7 +414,7 @@ public class SvnHistoryProvider implements VcsHistoryProvider { } private class RepositoryLogEntryHandler extends MyLogEntryHandler { - public RepositoryLogEntryHandler(final String url, final SVNRevision pegRevision, String lastPath, final ArrayList result) { + public RepositoryLogEntryHandler(final String url, final SVNRevision pegRevision, String lastPath, final Consumer result) { super(url, pegRevision, lastPath, result); }