diff options
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.java | 213 |
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); + } + + } + } |