import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.util.Pair;
import javax.swing.*;
import java.awt.*;
-import java.awt.event.InputEvent;
import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
/**
return getTrailingRow(list, visibleRect) - getLeadingRow(list, visibleRect) + 1;
}
+ public static Pair<Integer, Integer> getVisibleRows(JTable list) {
+ Rectangle visibleRect = list.getVisibleRect();
+ return new Pair<Integer, Integer>(getLeadingRow(list, visibleRect) + 1, getTrailingRow(list, visibleRect));
+ }
+
private static int getLeadingRow(JTable table,Rectangle visibleRect) {
Point leadingPoint;
myTs = ts;
}
- public void flush() {
+ public void flushPart() {
if (! myBuffer.isEmpty()) {
myConsumer.consume(new ArrayList<T>(myBuffer));
myBuffer.clear();
}
+ }
+
+ public void flush() {
+ flushPart();
if (myFlushListener != null) {
myFlushListener.run();
}
import java.util.Set;
public class SLRUMap<K,V> {
- private final Map<K,V> myProtectedQueue;
- private final Map<K,V> myProbationalQueue;
+ protected final Map<K,V> myProtectedQueue;
+ protected final Map<K,V> myProbationalQueue;
private final int myProtectedQueueSize;
private final int myProbationalQueueSize;
h.endOptions();
h.addRelativePaths(path);
String output = h.run();
+ return parseCommitsLoadOutput(allBranchesSet, root, output);
+ }
+
+ public static List<GitCommit> commitsDetails(Project project,
+ FilePath path, Set<String> allBranchesSet,
+ final Collection<String> commitsIds) throws VcsException {
+ // adjust path using change manager
+ path = getLastCommitName(project, path);
+ final VirtualFile root = GitUtil.getGitRoot(path);
+ GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.SHOW);
+ h.setNoSSH(true);
+ h.setStdoutSuppressed(true);
+ h.addParameters("--name-only",
+ "--pretty=format:%x03%h%x00%H%x00%ct%x00%an%x00%ae%x00%cn%x00%ce%x00[%p]%x00[%d]%x00%s%n%n%b%x00", "--encoding=UTF-8");
+ h.addParameters(new ArrayList<String>(commitsIds));
+
+ h.endOptions();
+ h.addRelativePaths(path);
+ String output = h.run();
+ return parseCommitsLoadOutput(allBranchesSet, root, output);
+ }
+
+ private static List<GitCommit> parseCommitsLoadOutput(Set<String> allBranchesSet, VirtualFile root, String output) throws VcsException {
final List<GitCommit> rc = new ArrayList<GitCommit>();
StringTokenizer tk = new StringTokenizer(output, "\u0003", false);
final String prefix = root.getPath() + "/";
void cherryPick(SHAHash hash) throws VcsException;
void loadHashesWithParents(final @NotNull Collection<String> startingPoints, @NotNull final Collection<ChangesFilter.Filter> filters,
final Consumer<CommitHashPlusParents> consumer) throws VcsException;
+ List<GitCommit> getCommitDetails(final Collection<String> commitIds) throws VcsException;
}
GitHistoryUtils.hashesWithParents(myProject, new FilePathImpl(myRoot), consumer, parameters.toArray(new String[parameters.size()]));
}
+ @Override
+ public List<GitCommit> getCommitDetails(final Collection<String> commitIds) throws VcsException {
+ // todo branches
+ return GitHistoryUtils.commitsDetails(myProject, new FilePathImpl(myRoot), Collections.<String>emptySet(), commitIds);
+ }
+
public void loadCommits(final Collection<String> startingPoints, final Date beforePoint, final Date afterPoint,
final Collection<ChangesFilter.Filter> filtersIn, final Consumer<GitCommit> consumer,
int maxCnt, List<String> branches) throws VcsException {
--- /dev/null
+/*
+ * 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 git4idea.history.wholeTree;
+
+import com.intellij.util.containers.SLRUMap;
+
+import java.util.*;
+
+/**
+ * @author irengrig
+ */
+public class CommitIdsHolder<Item> {
+ private final MyLruCache<Item, Item> myRequests;
+ private final Object myLock;
+
+ public CommitIdsHolder() {
+ myRequests = new MyLruCache<Item, Item>(100, 50);
+ myLock = new Object();
+ }
+
+ public void add(final Collection<Item> s) {
+ synchronized (myLock) {
+ for (Item hash : s) {
+ myRequests.put(hash, hash);
+ }
+ }
+ }
+
+ public boolean haveData() {
+ synchronized (myLock) {
+ return ! myRequests.isEmpty();
+ }
+ }
+
+ public Collection<Item> get(final int size) {
+ final Set<Item> result = new HashSet<Item>();
+ synchronized (myLock) {
+ final Iterator<Item> iterator = myRequests.iterator();
+ int cnt = 0;
+ for (; iterator.hasNext() && (cnt < size);) {
+ final Item hash = iterator.next();
+ iterator.remove();
+ result.add(hash);
+ ++ cnt;
+ }
+ }
+ return result;
+ }
+
+ private static class MyLruCache<Key, Val> extends SLRUMap<Key, Val> {
+ private MyLruCache(int protectedQueueSize, int probationalQueueSize) {
+ super(protectedQueueSize, probationalQueueSize);
+ }
+
+ public boolean isEmpty() {
+ return myProtectedQueue.keySet().isEmpty() && myProbationalQueue.keySet().isEmpty();
+ }
+
+ public Iterator<Key> iterator() {
+ final List<Iterator<Key>> iterators = new ArrayList<Iterator<Key>>(2);
+ iterators.add(myProtectedQueue.keySet().iterator());
+ iterators.add(myProbationalQueue.keySet().iterator());
+ return new CompositeIterator<Key>(iterators);
+ }
+
+ private static class CompositeIterator<Key> implements Iterator<Key> {
+ private int myPreviousIdx;
+ private int myIdx;
+ private final List<Iterator<Key>> myIterators;
+
+ private CompositeIterator(final List<Iterator<Key>> iterators) {
+ myIterators = iterators;
+ myIdx = -1;
+ myPreviousIdx = -1;
+ for (int i = 0; i < myIterators.size(); i++) {
+ final Iterator<Key> iterator = myIterators.get(i);
+ if (iterator.hasNext()) {
+ myIdx = i;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return (myIdx >= 0) && myIterators.get(myIdx).hasNext();
+ }
+
+ @Override
+ public Key next() {
+ final Key result = myIterators.get(myIdx).next();
+ recalculateCurrent();
+ return result;
+ }
+
+ private void recalculateCurrent() {
+ if (myIdx == -1) return;
+ if (! myIterators.get(myIdx).hasNext()) {
+ myPreviousIdx = myIdx;
+ myIdx = -1;
+ for (int i = myPreviousIdx; i < myIterators.size(); i++) {
+ final Iterator<Key> iterator = myIterators.get(i);
+ if (iterator.hasNext()) {
+ myIdx = i;
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void remove() {
+ if ((myPreviousIdx != -1) && (myPreviousIdx != myIdx)) {
+ // last element
+ final Iterator<Key> keyIterator = myIterators.get(myPreviousIdx);
+ keyIterator.remove(); // already on last position
+ } else {
+ myIterators.get(myIdx).remove();
+ }
+ recalculateCurrent();
+ }
+ }
+ }
+}
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.CompoundNumber;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.TableScrollingUtil;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.UIUtil;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
+import java.awt.event.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
private JTextField myFilterField;
private String myPreviousFilter;
private JLabel myLoading;
+ private MyChangeListener myListener;
// todo for the case when hierarchy is also drawn?
public GitLogLongPanel(final Project project, final Collection<VirtualFile> roots) {
};
loaderImpl.setUIRefresh(myUIRefresh);
- initPanel();
+ initPanel(loaderImpl);
}
- private void initPanel() {
+ public void stop() {
+ myListener.stop();
+ }
+
+ private void initPanel(final LoaderImpl loaderImpl) {
final JPanel container = new JPanel(new BorderLayout());
final JPanel menu = new JPanel();
final BoxLayout layout = new BoxLayout(menu, BoxLayout.X_AXIS);
final JBTable table = new JBTable(myTableModel);
table.setModel(myTableModel);
final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table);
- scrollPane.getViewport().addChangeListener(new MyChangeListener());
+ myListener = new MyChangeListener(loaderImpl, table);
+ scrollPane.getViewport().addChangeListener(myListener);
container.add(menu, BorderLayout.NORTH);
container.add(scrollPane, BorderLayout.CENTER);
return myGitLogLongPanel.myPanel;
}
+ @Override
+ public void doCancelAction() {
+ myGitLogLongPanel.stop();
+ super.doCancelAction();
+ }
+
+ @Override
+ protected void doOKAction() {
+ myGitLogLongPanel.stop();
+ super.doOKAction();
+ }
+
@Override
public JComponent getPreferredFocusedComponent() {
myGitLogLongPanel.setModalityState(ModalityState.current());
}
private static class MyChangeListener implements ChangeListener {
+ private final Speedometer mySpeedometer;
+ private Timer myTimer;
+ private long myRefreshMark;
+
+ private MyChangeListener(final LoaderImpl loaderImpl, final JBTable table) {
+ mySpeedometer = new Speedometer();
+ myRefreshMark = 0;
+ myTimer = new Timer(100, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ //final boolean shouldPing = (System.currentTimeMillis() - myRefreshMark) > 700;
+ final boolean shouldPing = false;
+ if (((mySpeedometer.getSpeed() < 0.1) && mySpeedometer.hasData()) || shouldPing) {
+ myRefreshMark = System.currentTimeMillis();
+ mySpeedometer.clear();
+ final Pair<Integer,Integer> visibleRows = TableScrollingUtil.getVisibleRows(table);
+ loaderImpl.loadCommitDetails(visibleRows.first == 0 ? visibleRows.first : (visibleRows.first - 1), visibleRows.second);
+ }
+ }
+ });
+ myTimer.start();
+ }
+
+ public void stop() {
+ myTimer.stop();
+ }
+
@Override
public void stateChanged(ChangeEvent e) {
- System.out.println(e.toString());
+ mySpeedometer.event();
}
}
}
myCache = new SLRUMap<String, GitCommit>(ourSize, 50);
}
+ public boolean shouldLoad(int idx) {
+ final VisibleLine visibleLine = myTreeComposite.get(idx);
+ if (visibleLine.isDecoration()) {
+ return false;
+ }
+ final String hash = new String(((TreeSkeletonImpl.Commit) visibleLine.getData()).getHash());
+ final GitCommit gitCommit = myCache.get(hash);
+ return gitCommit == null;
+ }
+
@Override
public Object get(int idx) {
final VisibleLine visibleLine = myTreeComposite.get(idx);
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator;
+import com.intellij.openapi.progress.BackgroundTaskQueue;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.CompoundNumber;
import com.intellij.openapi.vcs.StaticReadonlyList;
import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.BufferedListConsumer;
import com.intellij.util.Consumer;
import com.intellij.util.containers.Convertor;
+import com.intellij.util.containers.MultiMap;
import git4idea.history.browser.ChangesFilter;
import git4idea.history.browser.GitCommit;
import git4idea.history.browser.LowLevelAccess;
import git4idea.history.browser.LowLevelAccessImpl;
+import org.jetbrains.annotations.NotNull;
import java.util.*;
* @author irengrig
*/
public class LoaderImpl implements Loader {
- private static final long ourTestTimeThreshold = 500;
+ private static final Logger LOG = Logger.getInstance("#git4idea.history.wholeTree.LoaderImpl");
+
+ private static final long ourTestTimeThreshold = 100;
private final static int ourBigArraysSize = 10;
private final static int ourTestCount = 5;
- // todo Object +-
+ private final static int ourSlowPreloadCount = 50;
private final TreeComposite<VisibleLine> myTreeComposite;
private final Map<VirtualFile, LowLevelAccess> myAccesses;
private final LinesProxy myLinesCache;
private final Project myProject;
private ModalityState myModalityState;
- public LoaderImpl(final Project project,
- final Collection<VirtualFile> allGitRoots) {
+ private final BackgroundTaskQueue myQueue;
+ private final CommitIdsHolder<Pair<VirtualFile, String>> myCommitIdsHolder;
+ private List<VirtualFile> myRootsList;
+
+ public LoaderImpl(final Project project, final Collection<VirtualFile> allGitRoots) {
myProject = project;
myTreeComposite = new TreeComposite<VisibleLine>(ourBigArraysSize, WithoutDecorationComparator.getInstance());
myLinesCache = new LinesProxy(myTreeComposite);
for (VirtualFile gitRoot : allGitRoots) {
myAccesses.put(gitRoot, new LowLevelAccessImpl(project, gitRoot));
}
+ // todo refresh roots
+ myRootsList = new ArrayList<VirtualFile>(myAccesses.keySet());
+ Collections.sort(myRootsList, FilePathComparator.getInstance());
myLock = new Object();
myLoadId = 0;
+ myQueue = new BackgroundTaskQueue(project, "Git log");
+ myCommitIdsHolder = new CommitIdsHolder();
}
public LinesProxy getLinesProxy() {
}
final boolean drawHierarchy = filters.isEmpty();
- application.executeOnPooledThread(new Runnable() {
+ myQueue.run(new Task.Backgroundable(myProject, "Git log refresh", false, BackgroundFromStartOption.getInstance()) {
@Override
- public void run() {
+ public void run(@NotNull ProgressIndicator indicator) {
try {
final Join join = new Join(myAccesses.size(), new MyJoin(current));
final Runnable joinCaller = new Runnable() {
myTreeComposite.clearMembers();
- final List<VirtualFile> list = new ArrayList<VirtualFile>(myAccesses.keySet());
- Collections.sort(list, FilePathComparator.getInstance());
-
- for (VirtualFile vf : list) {
+ final List<LoaderBase> endOfTheList = new LinkedList<LoaderBase>();
+ for (VirtualFile vf : myRootsList) {
final LowLevelAccess access = myAccesses.get(vf);
final Consumer<CommitHashPlusParents> consumer = createCommitsHolderConsumer(drawHierarchy);
final Consumer<List<CommitHashPlusParents>> listConsumer = new RefreshingCommitsPackConsumer(current, consumer);
final BufferedListConsumer<CommitHashPlusParents> bufferedListConsumer =
- new BufferedListConsumer<CommitHashPlusParents>(15, listConsumer, 1000);
+ new BufferedListConsumer<CommitHashPlusParents>(15, listConsumer, 400);
bufferedListConsumer.setFlushListener(joinCaller);
final long start = System.currentTimeMillis();
FullDataLoader.load(myLinesCache, access, startingPoints, filters, bufferedListConsumer.asConsumer(), ourTestCount);
final long end = System.currentTimeMillis();
+ bufferedListConsumer.flushPart();
if (allDataAlreadyLoaded) {
bufferedListConsumer.flush();
} else {
final boolean loadFull = (end - start) > ourTestTimeThreshold;
- final LoaderBase loaderBase = new LoaderBase(access, bufferedListConsumer, filters, ourTestCount, loadFull, startingPoints, myLinesCache);
- loaderBase.execute();
+ if (loadFull) {
+ final LoaderBase loaderBase = new LoaderBase(access, bufferedListConsumer, filters,
+ ourTestCount, loadFull, startingPoints, myLinesCache, ourSlowPreloadCount);
+ loaderBase.execute();
+ endOfTheList.add(new LoaderBase(access, bufferedListConsumer, filters, ourSlowPreloadCount, false, startingPoints, myLinesCache, -1));
+ } else {
+ final LoaderBase loaderBase = new LoaderBase(access, bufferedListConsumer, filters, ourTestCount, loadFull, startingPoints, myLinesCache, -1);
+ loaderBase.execute();
+ }
}
}
+ myQueue.run(new Task.Backgroundable(myProject, "Git log refresh", false, BackgroundFromStartOption.getInstance()) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ for (LoaderBase loaderBase : endOfTheList) {
+ try {
+ loaderBase.execute();
+ }
+ catch (VcsException e) {
+ myUIRefresh.acceptException(e);
+ }
+ }
+ }});
} catch (VcsException e) {
myUIRefresh.acceptException(e);
+ } catch (MyStopListenToOutputException e) {
} finally {
//myUIRefresh.skeletonLoadComplete();
}
consumer = new Consumer<CommitHashPlusParents>() {
@Override
public void consume(CommitHashPlusParents commitHashPlusParents) {
- readonlyList.consume(new TreeSkeletonImpl.Commit(commitHashPlusParents.getHash().getBytes(), 0, commitHashPlusParents.getTime()));
+ readonlyList.consume(new TreeSkeletonImpl.Commit(commitHashPlusParents.getHash(), 0, commitHashPlusParents.getTime()));
}
};
myTreeComposite.addMember(readonlyList);
myRefreshRunnable = new Runnable() {
@Override
public void run() {
+ if (myTreeComposite.getAwaitedSize() == myTreeComposite.getSize()) return;
+ LOG.info("Items refresh: (was=" + myTreeComposite.getSize() + " will be=" + myTreeComposite.getAwaitedSize());
myTreeComposite.repack();
if (! mySomeDataShown) {
// todo remove
private final LowLevelAccess myAccess;
private final Collection<String> myStartingPoints;
private final Consumer<GitCommit> myLinesCache;
+ private final int myMaxCount;
private final Collection<ChangesFilter.Filter> myFilters;
private final int myIgnoreFirst;
public LoaderBase(LowLevelAccess access,
BufferedListConsumer<CommitHashPlusParents> consumer,
Collection<ChangesFilter.Filter> filters,
- int ignoreFirst, boolean loadFullData, Collection<String> startingPoints, final Consumer<GitCommit> linesCache) {
+ int ignoreFirst, boolean loadFullData, Collection<String> startingPoints, final Consumer<GitCommit> linesCache,
+ final int maxCount) {
myAccess = access;
myConsumer = consumer;
myFilters = filters;
myLoadFullData = loadFullData;
myStartingPoints = startingPoints;
myLinesCache = linesCache;
+ myMaxCount = maxCount;
}
public void execute() throws VcsException {
- final MyConsumer consumer = new MyConsumer(myConsumer, myIgnoreFirst);
+ final MyConsumer consumer = new MyConsumer(myConsumer, 0);
if (myLoadFullData) {
- FullDataLoader.load(myLinesCache, myAccess, myStartingPoints, myFilters, myConsumer.asConsumer(), -1);
+ // todo
+ LOG.info("FULL " + myMaxCount);
+ FullDataLoader.load(myLinesCache, myAccess, myStartingPoints, myFilters, myConsumer.asConsumer(), myMaxCount);
} else {
+ LOG.info("SKELETON " + myMaxCount);
myAccess.loadHashesWithParents(myStartingPoints, myFilters, consumer);
}
myConsumer.flush();
return Comparing.compare(obj1.toString(), obj2.toString());
}
}
+
+ private final static int ourLoadSize = 30;
+
+ public void loadCommitDetails(final int startIdx, final int endIdx) {
+ final Set<Pair<VirtualFile, String>> newIds = new HashSet<Pair<VirtualFile, String>>();
+ for (int i = startIdx; i <= endIdx; i++) {
+ if (myLinesCache.shouldLoad(i)) {
+ final TreeSkeletonImpl.Commit commit = (TreeSkeletonImpl.Commit) myTreeComposite.get(i).getData();
+ final CompoundNumber memberData = myTreeComposite.getMemberData(i);
+ newIds.add(new Pair<VirtualFile, String>(myRootsList.get(memberData.getMemberNumber()), String.valueOf(commit.getHash())));
+ }
+ }
+
+ myCommitIdsHolder.add(newIds);
+ scheduleDetailsLoad();
+ }
+
+ private void scheduleDetailsLoad() {
+ final Collection<Pair<VirtualFile, String>> toLoad = myCommitIdsHolder.get(ourLoadSize);
+ if (toLoad.isEmpty()) return;
+ myQueue.run(new Task.Backgroundable(myProject, "Load git commits details", false, BackgroundFromStartOption.getInstance()) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ final MultiMap<VirtualFile, String> map = new MultiMap<VirtualFile, String>();
+ for (Pair<VirtualFile, String> pair : toLoad) {
+ map.putValue(pair.getFirst(), pair.getSecond());
+ }
+ for (VirtualFile virtualFile : map.keySet()) {
+ try {
+ final Collection<String> values = map.get(virtualFile);
+ final List<GitCommit> commitDetails = myAccesses.get(virtualFile).getCommitDetails(values);
+ for (GitCommit commitDetail : commitDetails) {
+ myLinesCache.consume(commitDetail);
+ }
+ // todo another UI event
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myUIRefresh.setSomeDataReadyState();
+ }
+ }, myModalityState);
+ }
+ catch (final VcsException e) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myUIRefresh.acceptException(e);
+ }
+ }, myModalityState);
+ }
+ }
+ if (myCommitIdsHolder.haveData()) {
+ scheduleDetailsLoad();
+ }
+ }
+ });
+ }
}
mySkeleton.addStartToEvent(commit.myIdx, rowCount);
}
- if (mySeizedWires.get(correspCommit.getWireNumber()) == commit.myIdx) {
+ final Integer seized = mySeizedWires.get(correspCommit.getWireNumber());
+ if (seized != null && seized == commit.myIdx) {
if (wireNumber == -1) {
// there is no other commits on the wire after parent -> use it
wireNumber = correspCommit.getWireNumber();
--- /dev/null
+/*
+ * 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 git4idea.history.wholeTree;
+
+import java.util.LinkedList;
+
+/**
+ * @author irengrig
+ */
+public class Speedometer {
+ private final int myHistorySize;
+ private final int myMillisInterval;
+ private final LinkedList<Long> myEvents;
+
+ public Speedometer() {
+ this(-1, -1);
+ }
+
+ public Speedometer(int historySize, int millisInterval) {
+ myHistorySize = historySize == -1 ? 20 : historySize;
+ myMillisInterval = millisInterval == -1 ? 500 : millisInterval;
+ myEvents = new LinkedList<Long>();
+ }
+
+ public void event() {
+ while (myEvents.size() >= myHistorySize) {
+ myEvents.removeLast();
+ }
+ myEvents.addFirst(System.currentTimeMillis());
+ }
+
+ // events per 100ms during last myMillisInterval OR last myHistorySize
+ public double getSpeed() {
+ if (myEvents.isEmpty()) return 0;
+
+ final long current = System.currentTimeMillis();
+ final long boundary = current - myMillisInterval;
+ int cnt = 0;
+ final long end = myEvents.getFirst();
+ long last = end;
+ for (Long event : myEvents) {
+ if (cnt > myHistorySize) break;
+ if (event < boundary) break;
+
+ ++ cnt;
+ last = event;
+ }
+ if (cnt == 0) return 0;
+ return ((double) end - last) / (cnt * 100);
+ }
+
+ public void clear() {
+ myEvents.clear();
+ }
+
+ public boolean hasData() {
+ return ! myEvents.isEmpty();
+ }
+}
}
}
+ // todo debug
+ public void visualDump() {
+ System.out.println("======= dump =======");
+ final int size = myCombinedList.getSize();
+ for (int i = 0; i < size; i++) {
+ final CompoundNumber elem = myCombinedList.get(i);
+ System.out.println("i=" + i + " member=" + elem.getMemberNumber() + " hash=" + myMembers.get(elem.getMemberNumber()).get(elem.getIdx()).toString());
+ }
+ }
+
@Override
public T get(int idx) {
synchronized (myLock) {
public int getSize() {
return myCombinedList.getSize();
}
+
+ public int getAwaitedSize() {
+ int result = 0;
+ for (ReadonlyList<T> member : myMembers) {
+ result += member.getSize();
+ }
+ return result;
+ }
}
}
public void addCommit(final int row, final String hash, final int wireNumber, final long time) {
- myList.put(row, new Commit(hash.getBytes(), wireNumber, time));
+ myList.put(row, new Commit(hash, wireNumber, time));
}
@Nullable
}
public static class Commit implements Comparable<Commit>, VisibleLine {
- private final byte[] myHash;
+ private final String myHash;
private int myWireNumber;
private final long myTime;
- public Commit(final byte[] hash, final int wireNumber, long time) {
+ public Commit(final String hash, final int wireNumber, long time) {
myHash = hash;
myWireNumber = wireNumber;
myTime = time;
}
+ @Override
+ public String toString() {
+ return myHash;
+ }
+
@Override
public Object getData() {
return this;
return false;
}
- public byte[] getHash() {
+ public String getHash() {
return myHash;
}
--- /dev/null
+/*
+ * 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 git4idea.tests;
+
+import git4idea.history.wholeTree.CommitIdsHolder;
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * @author irengrig
+ */
+public class CommitIdsTest extends TestCase {
+ public void testSimple() throws Exception {
+ final CommitIdsHolder<Integer> holder = new CommitIdsHolder<Integer>();
+
+ holder.add(Arrays.asList(1,2,3,4,5,6,7,8));
+ Assert.assertTrue(holder.haveData());
+ Collection<Integer> integers = holder.get(5);
+ Assert.assertEquals(5, integers.size());
+ Assert.assertTrue(holder.haveData());
+ integers = holder.get(20);
+ Assert.assertEquals(3, integers.size());
+ Assert.assertTrue(! holder.haveData());
+ }
+}