Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FileDiffContentProvider.java')
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FileDiffContentProvider.java213
1 files changed, 184 insertions, 29 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FileDiffContentProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FileDiffContentProvider.java
index 13e34074c9..1ed9ccf2b1 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FileDiffContentProvider.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FileDiffContentProvider.java
@@ -1,6 +1,7 @@
/*******************************************************************************
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2012, Robin Stocker <robin@nibor.org>
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -12,78 +13,232 @@
package org.eclipse.egit.ui.internal.history;
import java.io.IOException;
-import java.util.Set;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Objects;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
-import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.progress.UIJob;
/**
- * Content provider for {@link FileDiff} objects
+ * Content provider for {@link FileDiff} objects. The diffs are computed
+ * asynchronously in a background job.
*/
public class FileDiffContentProvider implements IStructuredContentProvider {
static final int INTERESTING_MARK_TREE_FILTER_INDEX = 0;
- private TreeWalk walk;
-
- private RevCommit commit;
-
private FileDiff[] diff;
private TreeFilter markTreeFilter = TreeFilter.ALL;
- private Repository repo;
+ private CommitFileDiffViewer viewer;
+
+ private boolean needsRecompute;
+
+ private FileDiffLoader loader;
+
+ private FileDiffInput currentInput;
@Override
public void inputChanged(final Viewer newViewer, final Object oldInput,
final Object newInput) {
+ cancel();
+ viewer = (CommitFileDiffViewer) newViewer;
if (newInput != null) {
- repo = ((CommitFileDiffViewer) newViewer).getRepository();
- walk = ((CommitFileDiffViewer) newViewer).getTreeWalk();
- commit = (RevCommit) newInput;
+ currentInput = (FileDiffInput) newInput;
+ setInterestingPaths(currentInput.getInterestingPaths());
} else {
- repo = null;
- walk = null;
- commit = null;
+ currentInput = null;
}
diff = null;
+ needsRecompute = true;
}
/**
* Set the paths which are interesting and should be highlighted in the view.
* @param interestingPaths
*/
- void setInterestingPaths(Set<String> interestingPaths) {
- if (interestingPaths != null)
+ void setInterestingPaths(Collection<String> interestingPaths) {
+ if (interestingPaths != null) {
this.markTreeFilter = PathFilterGroup.createFromStrings(interestingPaths);
- else
+ } else {
this.markTreeFilter = TreeFilter.ALL;
- // FileDiffs need to be updated
- this.diff = null;
+ }
+ needsRecompute = true;
}
@Override
public Object[] getElements(final Object inputElement) {
- if (diff == null && walk != null && commit != null)
- try {
- diff = FileDiff.compute(repo, walk, commit, markTreeFilter);
- } catch (IOException err) {
- Activator.handleError(NLS.bind(UIText.FileDiffContentProvider_errorGettingDifference,
- commit.getId()), err, false);
+ if (!needsRecompute) {
+ return diff != null ? diff : new Object[0];
+ }
+ needsRecompute = false;
+ FileDiffInput input = currentInput;
+ if (input == null) {
+ diff = null;
+ return new Object[0];
+ }
+ cancel();
+ FileDiffLoader job = new FileDiffLoader(input, markTreeFilter);
+ job.addJobChangeListener(new JobChangeAdapter() {
+ @Override
+ public void done(IJobChangeEvent event) {
+ if (!event.getResult().isOK()) {
+ return;
+ }
+ UIJob updater = new UpdateJob(MessageFormat.format(
+ UIText.FileDiffContentProvider_updatingFileDiffs,
+ input.getCommit().getName()), job);
+ updater.schedule();
}
- return diff != null ? diff : new Object[0];
+ });
+ job.setUser(false);
+ job.setSystem(true);
+ loader = job;
+ loader.schedule();
+ return new Object[0];
+ }
+
+ private void cancel() {
+ if (loader != null) {
+ loader.cancel();
+ loader = null;
+ }
}
@Override
public void dispose() {
- // Nothing.
+ cancel();
+ viewer = null;
+ diff = null;
+ currentInput = null;
}
+
+ private static class FileDiffLoader extends Job {
+
+ private FileDiff[] diffs;
+
+ private final FileDiffInput input;
+
+ private final TreeFilter filter;
+
+ public FileDiffLoader(FileDiffInput input, TreeFilter filter) {
+ super(MessageFormat.format(
+ UIText.FileDiffContentProvider_computingFileDiffs,
+ input.getCommit().getName()));
+ this.input = input;
+ this.filter = filter;
+ setRule(new TreeWalkSchedulingRule(input.getTreeWalk()));
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ diffs = FileDiff.compute(input.getRepository(),
+ input.getTreeWalk(),
+ input.getCommit(), monitor, filter);
+ } catch (IOException err) {
+ Activator.handleError(MessageFormat.format(
+ UIText.FileDiffContentProvider_errorGettingDifference,
+ input.getCommit().getId()), err, false);
+ }
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ return Status.OK_STATUS;
+ }
+
+ public FileDiff[] getDiffs() {
+ return diffs;
+ }
+ }
+
+ private class UpdateJob extends UIJob {
+
+ FileDiffLoader loadJob;
+
+ public UpdateJob(String name, FileDiffLoader loadJob) {
+ super(name);
+ this.loadJob = loadJob;
+ }
+
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (viewer.getControl().isDisposed() || loader != loadJob) {
+ return Status.CANCEL_STATUS;
+ }
+ diff = loadJob.getDiffs();
+ viewer.refresh();
+ FileDiff interesting = getFirstInterestingElement();
+ if (interesting != null) {
+ if (currentInput.isSelectMarked()) {
+ viewer.setSelection(new StructuredSelection(interesting),
+ true);
+ } else {
+ viewer.reveal(interesting);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ private FileDiff getFirstInterestingElement() {
+ FileDiff[] diffs = diff;
+ if (diffs != null) {
+ for (FileDiff d : diffs) {
+ if (d.isMarked(INTERESTING_MARK_TREE_FILTER_INDEX)) {
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ }
+
+ /**
+ * Serializes all load jobs using the same tree walk. Tree walks are not
+ * thread safe.
+ */
+ private static class TreeWalkSchedulingRule implements ISchedulingRule {
+
+ private final TreeWalk treeWalk;
+
+ public TreeWalkSchedulingRule(TreeWalk treeWalk) {
+ this.treeWalk = treeWalk;
+ }
+
+ @Override
+ public boolean contains(ISchedulingRule rule) {
+ if (rule instanceof TreeWalkSchedulingRule) {
+ return Objects.equals(treeWalk,
+ ((TreeWalkSchedulingRule) rule).treeWalk);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isConflicting(ISchedulingRule rule) {
+ return contains(rule);
+ }
+
+ }
+
}

Back to the top