diff options
author | Michael Keppler | 2018-08-05 12:50:46 +0000 |
---|---|---|
committer | Matthias Sohn | 2021-01-12 21:03:52 +0000 |
commit | d5c654a008469ac03f286ef539b511d6ebb96e67 (patch) | |
tree | 6198ab50d3fa37dae6d7f03e43f0c1cef0fb0d53 | |
parent | 017a3af1da4166f269e06e5055f716523200cfa4 (diff) | |
download | egit-d5c654a008469ac03f286ef539b511d6ebb96e67.tar.gz egit-d5c654a008469ac03f286ef539b511d6ebb96e67.tar.xz egit-d5c654a008469ac03f286ef539b511d6ebb96e67.zip |
Property sheet support for tags
When selecting a tag in the repository view, the properties view now
shows information about the tag and its target (recursively in case
the tag points at another tag).
The object pointed to is shown as a nested property, which itself is
expandable if it's a tag or commit. PersonIdents for taggers and also
for commit authors and committers are shown also as expandable nested
properties, with sub-nodes for name, e-mail, and the date formatted
according to the EGit date preferences.
When a tag's properties are shown, the property page refreshes on date
format changes.
Deferred to follow-up changes:
* Showing information about the signature of signed tags; that needs
signature verification in JGit first.
* Showing the full tag or commit message if different from the short
message.
Bug: 537695
Change-Id: Ie8c3a51a4f07795a2d28b561ce2d53668b20c723
Signed-off-by: Michael Keppler <Michael.Keppler@gmx.de>
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
6 files changed, 527 insertions, 19 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java index 8071168a17..16d2afcd1c 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java @@ -4580,6 +4580,51 @@ public class UIText extends NLS { public static String TagAction_taggingFailed; /** */ + public static String CommitPropertySource_CommitAuthor; + + /** */ + public static String CommitPropertySource_CommitId; + + /** */ + public static String CommitPropertySource_CommitCategory; + + /** */ + public static String CommitPropertySource_Committer; + + /** */ + public static String CommitPropertySource_CommitMessage; + + /** */ + public static String TagPropertySource_LightweightTag; + + /** */ + public static String TagPropertySource_TagCategory; + + /** */ + public static String TagPropertySource_TagId; + + /** */ + public static String TagPropertySource_TagMessage; + + /** */ + public static String TagPropertySource_TagName; + + /** */ + public static String TagPropertySource_TagTagger; + + /** */ + public static String TagPropertySource_TagTarget; + + /** */ + public static String PersonIdentPropertySource_Name; + + /** */ + public static String PersonIdentPropertySource_Email; + + /** */ + public static String PersonIdentPropertySource_Date; + + /** */ public static String CreateTagDialog_tagName; /** */ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/CommitPropertySource.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/CommitPropertySource.java new file mode 100644 index 0000000000..bbdea2ce8b --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/CommitPropertySource.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.egit.ui.internal.repository; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.egit.ui.internal.UIText; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.PropertyDescriptor; + +/** + * Properties for a commit (read-only). + */ +public class CommitPropertySource implements IPropertySource { + + private static final String PROPERTY_COMMIT_MESSAGE = "commit_message"; //$NON-NLS-1$ + + private static final String PROPERTY_COMMIT_ID = "commit_id"; //$NON-NLS-1$ + + private static final String PROPERTY_COMMIT_AUTHOR = "commit_author"; //$NON-NLS-1$ + + private static final String PROPERTY_COMMITTER = "committer"; //$NON-NLS-1$ + + private RevCommit commit; + + private final IPropertyDescriptor[] descriptors; + + /** + * Create a property source for a tag. + * + * @param commit + * to show + */ + public CommitPropertySource(RevCommit commit) { + this.commit = commit; + List<PropertyDescriptor> result = new ArrayList<>(); + result.add(new PropertyDescriptor(PROPERTY_COMMIT_ID, + UIText.CommitPropertySource_CommitId)); + result.add(new PropertyDescriptor(PROPERTY_COMMIT_MESSAGE, + UIText.CommitPropertySource_CommitMessage)); + result.add(new PropertyDescriptor(PROPERTY_COMMIT_AUTHOR, + UIText.CommitPropertySource_CommitAuthor)); + result.add(new PropertyDescriptor(PROPERTY_COMMITTER, + UIText.CommitPropertySource_Committer)); + for (PropertyDescriptor p : result) { + p.setCategory(UIText.CommitPropertySource_CommitCategory); + } + descriptors = result.toArray(new IPropertyDescriptor[0]); + } + + @Override + public Object getEditableValue() { + return this; + } + + @Override + public IPropertyDescriptor[] getPropertyDescriptors() { + return descriptors; + } + + @Override + public Object getPropertyValue(Object id) { + switch (id.toString()) { + case PROPERTY_COMMIT_ID: + return commit.getName(); + case PROPERTY_COMMIT_MESSAGE: + // TODO: figure out a way to show the full message, if different + // from the short message. + return commit.getShortMessage(); + case PROPERTY_COMMIT_AUTHOR: + return new PersonIdentPropertySource(commit.getAuthorIdent()); + case PROPERTY_COMMITTER: + return new PersonIdentPropertySource(commit.getCommitterIdent()); + default: + return null; + } + } + + @Override + public boolean isPropertySet(Object id) { + return false; + } + + @Override + public void resetPropertyValue(Object id) { + // read only + } + + @Override + public void setPropertyValue(Object id, Object value) { + // read only + } + + @Override + public String toString() { + return Constants.TYPE_COMMIT + ' ' + commit.name(); + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/PersonIdentPropertySource.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/PersonIdentPropertySource.java new file mode 100644 index 0000000000..9b6d158e16 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/PersonIdentPropertySource.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.egit.ui.internal.repository; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.egit.ui.internal.PreferenceBasedDateFormatter; +import org.eclipse.egit.ui.internal.UIText; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.PropertyDescriptor; + +/** + * An {@link IPropertySource} to show a {@link PersonIdent} in a property sheet. + */ +public class PersonIdentPropertySource implements IPropertySource { + + private static final String PROPERTY_NAME = "name"; //$NON-NLS-1$ + + private static final String PROPERTY_EMAIL = "e-mail"; //$NON-NLS-1$ + + private static final String PROPERTY_DATE = "date"; //$NON-NLS-1$ + + private final PersonIdent person; + + private final IPropertyDescriptor[] descriptors; + + /** + * Creates a new {@link PersonIdentPropertySource}. + * + * @param ident + * to show + */ + public PersonIdentPropertySource(PersonIdent ident) { + person = ident; + List<PropertyDescriptor> result = new ArrayList<>(3); + result.add(new PropertyDescriptor(PROPERTY_NAME, + UIText.PersonIdentPropertySource_Name)); + result.add(new PropertyDescriptor(PROPERTY_EMAIL, + UIText.PersonIdentPropertySource_Email)); + result.add(new PropertyDescriptor(PROPERTY_DATE, + UIText.PersonIdentPropertySource_Date)); + String category = ident.toExternalString(); + for (PropertyDescriptor desc : result) { + desc.setCategory(category); + } + descriptors = result.toArray(new IPropertyDescriptor[0]); + } + + @Override + public Object getEditableValue() { + return this; + } + + @Override + public IPropertyDescriptor[] getPropertyDescriptors() { + return descriptors; + } + + @Override + public Object getPropertyValue(Object id) { + switch (id.toString()) { + case PROPERTY_NAME: + return person.getName(); + case PROPERTY_EMAIL: + return person.getEmailAddress(); + case PROPERTY_DATE: + return PreferenceBasedDateFormatter.create().formatDate(person); + default: + return null; + } + } + + @Override + public boolean isPropertySet(Object id) { + return false; + } + + @Override + public void resetPropertyValue(Object id) { + // read-only + } + + @Override + public void setPropertyValue(Object id, Object value) { + // read-only + } + + @Override + public String toString() { + return person.toExternalString(); + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryPropertySourceProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryPropertySourceProvider.java index 75ea6db8ca..02e039330c 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryPropertySourceProvider.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryPropertySourceProvider.java @@ -14,11 +14,13 @@ package org.eclipse.egit.ui.internal.repository; import org.eclipse.core.runtime.Adapters; +import org.eclipse.egit.ui.Activator; +import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode; import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType; import org.eclipse.jface.action.IToolBarManager; -import org.eclipse.jgit.events.ConfigChangedEvent; -import org.eclipse.jgit.events.ConfigChangedListener; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; @@ -40,12 +42,14 @@ public class RepositoryPropertySourceProvider implements private final PropertySheetPage myPage; + private final IPreferenceStore store; + private Object lastObject; private IPropertySource lastRepositorySource; private enum SourceType { - UNDEFINED, REPOSITORY, REMOTE, BRANCH + UNDEFINED, REPOSITORY, REMOTE, BRANCH, TAG } private SourceType lastSourceType = SourceType.UNDEFINED; @@ -54,12 +58,33 @@ public class RepositoryPropertySourceProvider implements private DisposeListener disposeListener; + private boolean listenToDateFormatChanges; + + private IPropertyChangeListener dateFormatListener = event -> { + if (!listenToDateFormatChanges) { + return; + } + String property = event.getProperty(); + if (property == null) { + return; + } + switch (property) { + case UIPreferences.DATE_FORMAT: + case UIPreferences.DATE_FORMAT_CHOICE: + refreshPage(); + break; + default: + break; + } + }; + /** * @param page * the page */ public RepositoryPropertySourceProvider(PropertySheetPage page) { myPage = page; + store = Activator.getDefault().getPreferenceStore(); } private void registerDisposal() { @@ -75,6 +100,9 @@ public class RepositoryPropertySourceProvider implements @Override public void widgetDisposed(DisposeEvent e) { removeListener(); + if (listenToDateFormatChanges) { + store.removePropertyChangeListener(dateFormatListener); + } } }; control.addDisposeListener(disposeListener); @@ -86,6 +114,12 @@ public class RepositoryPropertySourceProvider implements handle.remove(); } + private void refreshPage() { + lastObject = null; + myPage.getSite().getShell().getDisplay() + .asyncExec(() -> myPage.setPropertySourceProvider(this)); + } + @Override public IPropertySource getPropertySource(Object object) { @@ -109,20 +143,7 @@ public class RepositoryPropertySourceProvider implements RepositoryTreeNode node = (RepositoryTreeNode) object; listenerHandle = node.getRepository().getListenerList() - .addConfigChangedListener(new ConfigChangedListener() { - @Override - public void onConfigChanged(ConfigChangedEvent event) { - // force a refresh of the page - lastObject = null; - myPage.getSite().getShell().getDisplay().asyncExec(new Runnable() { - - @Override - public void run() { - myPage.setPropertySourceProvider(RepositoryPropertySourceProvider.this); - } - }); - } - }); + .addConfigChangedListener(event -> refreshPage()); if (node.getType() == RepositoryTreeNodeType.REPO) { lastObject = object; @@ -150,8 +171,14 @@ public class RepositoryPropertySourceProvider implements return lastRepositorySource; } return null; - } else - return null; + } else if (node.getType() == RepositoryTreeNodeType.TAG) { + lastObject = object; + checkChangeType(SourceType.TAG); + lastRepositorySource = new TagPropertySource(node.getRepository(), + (Ref) node.getObject()); + return lastRepositorySource; + } + return null; } private void checkChangeType(SourceType type) { @@ -172,6 +199,15 @@ public class RepositoryPropertySourceProvider implements // removed. bars.updateActionBars(); } + if (lastSourceType == SourceType.TAG) { + // Remove date format listener + store.removePropertyChangeListener(dateFormatListener); + listenToDateFormatChanges = false; + } else if (type == SourceType.TAG) { + // Add date format listener + listenToDateFormatChanges = true; + store.addPropertyChangeListener(dateFormatListener); + } } lastSourceType = type; } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/TagPropertySource.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/TagPropertySource.java new file mode 100644 index 0000000000..f4bd09d9d7 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/TagPropertySource.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2018, 2021 Michael Keppler and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Keppler - initial implementation + *******************************************************************************/ +package org.eclipse.egit.ui.internal.repository; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.egit.ui.Activator; +import org.eclipse.egit.ui.internal.UIText; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevTag; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.PropertyDescriptor; + +/** + * Properties for a tag (read-only). + */ +public class TagPropertySource implements IPropertySource { + + private static final String PROPERTY_TAG_ID = "tag_id"; //$NON-NLS-1$ + + private static final String PROPERTY_TAG_MESSAGE = "tag_message"; //$NON-NLS-1$ + + private static final String PROPERTY_TAG_NAME = "tag_name"; //$NON-NLS-1$ + + private static final String PROPERTY_TAG_TAGGER = "tag_tagger"; //$NON-NLS-1$ + + private static final String PROPERTY_TAG_TARGET = "tag_target"; //$NON-NLS-1$ + + private RevTag tag; + + private RevCommit commit; + + private Repository repository; + + private final String name; + + private final IPropertyDescriptor[] descriptors; + + /** + * Create a property source for a tag from a {@link Ref}. + * + * @param repo + * repository + * @param ref + * ref of the tag + */ + public TagPropertySource(Repository repo, Ref ref) { + this.repository = repo; + try (RevWalk refWalk = new RevWalk(repo)) { + ObjectId objectId = repo.resolve(ref.getName()); + RevObject any = refWalk.parseAny(objectId); + if (any instanceof RevTag) { + tag = (RevTag) any; + refWalk.parseBody(tag.getObject()); + } else { + RevObject peeledObject = refWalk.peel(any); + if (peeledObject instanceof RevCommit) { + commit = (RevCommit) peeledObject; + } + } + } catch (IOException e) { + Activator.handleError(e.getMessage(), e, true); + } + name = tag != null ? tag.getTagName() : Repository.shortenRefName(ref.getName()); + descriptors = createDescriptors(); + } + + /** + * Create a property source for a {@link RevTag}. + * + * @param repo + * repository + * @param tag + * to show + */ + public TagPropertySource(Repository repo, RevTag tag) { + this.repository = repo; + this.tag = tag; + name = tag.getName(); + try (RevWalk refWalk = new RevWalk(repo)) { + refWalk.parseBody(tag.getObject()); + } catch (IOException e) { + Activator.handleError(e.getMessage(), e, true); + } + descriptors = createDescriptors(); + } + + private IPropertyDescriptor[] createDescriptors() { + List<PropertyDescriptor> result = new ArrayList<>(); + result.add(new PropertyDescriptor(PROPERTY_TAG_NAME, + UIText.TagPropertySource_TagName)); + if (tag == null) { + if (commit != null) { + result.add(new PropertyDescriptor(PROPERTY_TAG_TARGET, + UIText.TagPropertySource_TagTarget)); + } + } else { + result.add(new PropertyDescriptor(PROPERTY_TAG_ID, + UIText.TagPropertySource_TagId)); + result.add(new PropertyDescriptor(PROPERTY_TAG_MESSAGE, + UIText.TagPropertySource_TagMessage)); + result.add(new PropertyDescriptor(PROPERTY_TAG_TAGGER, + UIText.TagPropertySource_TagTagger)); + result.add(new PropertyDescriptor(PROPERTY_TAG_TARGET, + UIText.TagPropertySource_TagTarget)); + } + for (PropertyDescriptor p : result) { + p.setCategory(UIText.TagPropertySource_TagCategory); + } + return result.toArray(new IPropertyDescriptor[0]); + } + + @Override + public Object getEditableValue() { + return this; + } + + @Override + public IPropertyDescriptor[] getPropertyDescriptors() { + return descriptors; + } + + @Override + public Object getPropertyValue(Object id) { + switch (id.toString()) { + case PROPERTY_TAG_ID: + return tag.name(); + case PROPERTY_TAG_NAME: + return name; + case PROPERTY_TAG_TARGET: + if (tag != null) { + RevObject target = tag.getObject(); + if (target instanceof RevTag) { + return new TagPropertySource(repository, (RevTag) target); + } else if (target instanceof RevCommit) { + return new CommitPropertySource((RevCommit) target); + } + return Constants.typeString(target.getType()) + ' ' + + target.name(); + } + if (commit != null) { + return new CommitPropertySource(commit); + } + return null; + case PROPERTY_TAG_TAGGER: + return new PersonIdentPropertySource(tag.getTaggerIdent()); + case PROPERTY_TAG_MESSAGE: + // TODO: figure out a way to show the full message, if different + // from the short message. + return tag.getShortMessage(); + default: + return null; + } + } + + @Override + public boolean isPropertySet(Object id) { + return false; + } + + @Override + public void resetPropertyValue(Object id) { + // read only + } + + @Override + public void setPropertyValue(Object id, Object value) { + // read only + } + + @Override + public String toString() { + return tag != null ? Constants.TYPE_TAG + ' ' + tag.name() + : MessageFormat.format(UIText.TagPropertySource_LightweightTag, + name); + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties index ba85a47b15..4423c6b4fe 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties @@ -1574,6 +1574,22 @@ TagAction_errorWhileGettingRevCommits=An error occurred while getting list of co TagAction_creating=Creating tag ''{0}'' TagAction_taggingFailed=Could not create tag ''{0}'' +CommitPropertySource_CommitAuthor=Author +CommitPropertySource_CommitCategory=Commit +CommitPropertySource_CommitId=Commit ID +CommitPropertySource_CommitMessage=Commit message +CommitPropertySource_Committer=Committer +TagPropertySource_LightweightTag=Lightweight Tag ''{0}'' +TagPropertySource_TagCategory=Tag +TagPropertySource_TagId=Tag ID +TagPropertySource_TagMessage=Tag message +TagPropertySource_TagName=Tag name +TagPropertySource_TagTagger=Tag created by +TagPropertySource_TagTarget=Tag target +PersonIdentPropertySource_Name=Name +PersonIdentPropertySource_Email=E-mail address +PersonIdentPropertySource_Date=Timestamp + CreateTagDialog_tagName=Tag &name: CreateTagDialog_tagMessage=Tag &message: CreateTagDialog_questionNewTagTitle=Create New Tag on Branch ''{0}'' |