From 47eb2f74cf0a221c70c014819079f5be6f1ac32b Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Sat, 5 Jun 2021 11:02:47 +0200 Subject: [PATCH] [merge] Use a compact tree for the directory structure Create a compact model for the DiffTreeViewer. The structure compare pane in an Eclipse compare editor is typically rather small. Using a compact tree gives in many cases an easier to navigate tree. Change-Id: I60726a8bb8d98867913a293bd3fab62880bb1612 Signed-off-by: Thomas Wolf --- .../merge/AbstractGitMergeEditorInput.java | 119 ++++++++++++++++++--- .../ui/internal/merge/GitMergeEditorInput.java | 7 +- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/AbstractGitMergeEditorInput.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/AbstractGitMergeEditorInput.java index 494e4e28a..a2908b356 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/AbstractGitMergeEditorInput.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/AbstractGitMergeEditorInput.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,6 +30,7 @@ import org.eclipse.compare.CompareViewerPane; import org.eclipse.compare.IResourceProvider; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.contentmergeviewer.ContentMergeViewer; +import org.eclipse.compare.structuremergeviewer.DiffContainer; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.compare.structuremergeviewer.Differencer; import org.eclipse.compare.structuremergeviewer.ICompareInput; @@ -60,7 +62,9 @@ import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.commands.ActionHandler; +import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jgit.lib.Repository; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; @@ -80,6 +84,13 @@ import org.eclipse.ui.services.IServiceLocator; @SuppressWarnings("restriction") public abstract class AbstractGitMergeEditorInput extends CompareEditorInput { + private static final Comparator CMP = (left, right) -> { + String l = left.startsWith("/") ? left.substring(1) : left; //$NON-NLS-1$ + String r = right.startsWith("/") ? right.substring(1) : right; //$NON-NLS-1$ + return l.replace('/', '\001') + .compareToIgnoreCase(r.replace('/', '\001')); + }; + private static final Image FOLDER_IMAGE = PlatformUI.getWorkbench() .getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); @@ -149,6 +160,26 @@ public abstract class AbstractGitMergeEditorInput extends CompareEditorInput { } @Override + public Viewer createDiffViewer(Composite parent) { + Viewer viewer = super.createDiffViewer(parent); + if (viewer instanceof StructuredViewer) { + ((StructuredViewer) viewer) + .setComparator(new ViewerComparator(CMP) { + + @Override + public int category(Object element) { + if (element instanceof FolderNode) { + return 0; + } else { + return 1; + } + } + }); + } + return viewer; + } + + @Override public Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent) { Viewer newViewer = super.findContentViewer(oldViewer, input, parent); @@ -447,23 +478,37 @@ public abstract class AbstractGitMergeEditorInput extends CompareEditorInput { return ((DiffNode) child); } } - DiffNode child = new DiffNode(parent, Differencer.NO_CHANGE) { - - @Override - public String getName() { - return name; - } + return new FolderNode(parent, name, + projectMode ? PROJECT_IMAGE : FOLDER_IMAGE); + } - @Override - public Image getImage() { - if (projectMode) { - return PROJECT_IMAGE; + private void collapse(DiffContainer top) { + IDiffElement[] children = top.getChildren(); + boolean isRoot = top.getParent() == null; + if (!isRoot) { + while (children != null && children.length == 1) { + IDiffElement singleChild = children[0]; + if (singleChild instanceof FolderNode) { + FolderNode node = (FolderNode) singleChild; + top.remove(singleChild); + top.getParent().add(singleChild); + node.setName(top.getName() + '/' + singleChild.getName()); + ((DiffContainer) top.getParent()).remove(top); + children = node.getChildren(); + top = node; } else { - return FOLDER_IMAGE; + // Hit a leaf. + return; } } - }; - return child; + } + if (children != null && (isRoot || children.length > 1)) { + for (IDiffElement node : children) { + if (node instanceof FolderNode) { + collapse((DiffContainer) node); + } + } + } } @Override @@ -481,7 +526,9 @@ public abstract class AbstractGitMergeEditorInput extends CompareEditorInput { if (monitor.isCanceled()) { throw new InterruptedException(); } - return buildInput(monitor); + DiffContainer result = buildInput(monitor); + collapse(result); + return result; } finally { monitor.done(); } @@ -499,7 +546,7 @@ public abstract class AbstractGitMergeEditorInput extends CompareEditorInput { * on cancellation * @see CompareEditorInput#prepareInput(IProgressMonitor monitor) */ - protected abstract Object buildInput(IProgressMonitor monitor) + protected abstract DiffContainer buildInput(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException; private void initPaths() throws InvocationTargetException { @@ -702,4 +749,46 @@ public abstract class AbstractGitMergeEditorInput extends CompareEditorInput { return super.hashCode(); } } + + private static class FolderNode extends DiffNode { + + private final Image image; + + private String name; + + FolderNode(IDiffContainer parent, String name, Image image) { + super(parent, Differencer.NO_CHANGE); + this.name = name; + this.image = image; + } + + @Override + public String getName() { + return name; + } + + void setName(String name) { + // Be careful when calling this. Changing the name of a node changes + // the hash code of this node and of all its children! Call only + // before the node's hashCode is needed. + this.name = name; + } + + @Override + public Image getImage() { + return image; + } + + @Override + public boolean equals(Object other) { + // Ignore my own fields. Super implementation includes getName(). + return super.equals(other); + } + + @Override + public int hashCode() { + // Ignore my own fields. Super implementation includes getName(). + return super.hashCode(); + } + } } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java index b26f90cab..cbeb05024 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java @@ -27,6 +27,7 @@ import org.eclipse.compare.CompareEditorInput; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.contentmergeviewer.ContentMergeViewer; import org.eclipse.compare.rangedifferencer.RangeDifference; +import org.eclipse.compare.structuremergeviewer.DiffContainer; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.compare.structuremergeviewer.Differencer; import org.eclipse.compare.structuremergeviewer.ICompareInput; @@ -162,7 +163,7 @@ public class GitMergeEditorInput extends AbstractGitMergeEditorInput { } @Override - protected Object buildInput(IProgressMonitor monitor) + protected DiffContainer buildInput(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { RevWalk rw = null; try { @@ -291,13 +292,13 @@ public class GitMergeEditorInput extends AbstractGitMergeEditorInput { } @SuppressWarnings("unused") - private IDiffContainer buildDiffContainer(Repository repository, + private DiffContainer buildDiffContainer(Repository repository, RevCommit headCommit, RevCommit ancestorCommit, RevWalk rw, IProgressMonitor monitor) throws IOException, InterruptedException { monitor.setTaskName(UIText.GitMergeEditorInput_CalculatingDiffTaskName); - IDiffContainer result = new DiffNode(Differencer.CONFLICTING); + DiffContainer result = new DiffNode(Differencer.CONFLICTING); ConflictStyle style = null; try (TreeWalk tw = new TreeWalk(repository)) { -- 2.11.4.GIT