Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScannerTest.java16
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScanner.java57
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java120
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java297
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FormatJob.java77
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java123
6 files changed, 439 insertions, 251 deletions
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScannerTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScannerTest.java
index 4de8b1ad1c..51e49cf31b 100644
--- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScannerTest.java
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScannerTest.java
@@ -101,7 +101,7 @@ public class HyperlinkTokenScannerTest {
IDocument testDocument = new Document(text);
when(viewer.getDocument()).thenReturn(testDocument);
HyperlinkTokenScanner scanner = new HyperlinkTokenScanner(detectors,
- viewer);
+ viewer, null);
scanner.setRangeAndColor(testDocument, offset, length, null);
IToken token = null;
char[] found = new char[text.length()];
@@ -110,15 +110,13 @@ public class HyperlinkTokenScannerTest {
int tokenOffset = scanner.getTokenOffset();
int tokenLength = scanner.getTokenLength();
char ch = 'x';
- if (token == HyperlinkTokenScanner.DEFAULT) {
+ Object data = token.getData();
+ if (data == null) {
ch = 'D';
- } else {
- Object data = token.getData();
- if (data instanceof TextAttribute) {
- int style = ((TextAttribute) data).getStyle();
- if ((style & TextAttribute.UNDERLINE) != 0) {
- ch = 'H';
- }
+ } else if (data instanceof TextAttribute) {
+ int style = ((TextAttribute) data).getStyle();
+ if ((style & TextAttribute.UNDERLINE) != 0) {
+ ch = 'H';
}
}
Arrays.fill(found, tokenOffset, tokenOffset + tokenLength, ch);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScanner.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScanner.java
index 5a754b8231..91517a5a6f 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScanner.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkTokenScanner.java
@@ -30,13 +30,6 @@ import org.eclipse.swt.graphics.Color;
*/
public class HyperlinkTokenScanner implements ITokenScanner {
- /** The {@link IToken} returned for all non-hyperlink content. */
- protected static final IToken DEFAULT = new Token(null);
-
- private int endOfRange;
-
- private int currentOffset;
-
private int tokenStart;
private IToken hyperlinkToken;
@@ -45,6 +38,18 @@ public class HyperlinkTokenScanner implements ITokenScanner {
private final IHyperlinkDetector[] hyperlinkDetectors;
+ /** The current offset in the document. */
+ protected int currentOffset;
+
+ /** The end of the range to tokenize. */
+ protected int endOfRange;
+
+ /** The {@link IDocument} the current scan operates on. */
+ protected IDocument document;
+
+ /** The {@link IToken} to use for default content. */
+ protected final IToken defaultToken;
+
/**
* Creates a new instance that uses the given hyperlink detector and viewer.
*
@@ -55,8 +60,25 @@ public class HyperlinkTokenScanner implements ITokenScanner {
*/
public HyperlinkTokenScanner(IHyperlinkDetector[] hyperlinkDetectors,
ISourceViewer viewer) {
+ this(hyperlinkDetectors, viewer, null);
+ }
+
+ /**
+ * Creates a new instance that uses the given hyperlink detector and viewer.
+ *
+ * @param hyperlinkDetectors
+ * the {@link IHyperlinkDetector}s to use
+ * @param viewer
+ * the {@link ISourceViewer} to operate in
+ * @param defaultAttribute
+ * the {@link TextAttribute} to use for the default token; may be
+ * {@code null} to use the default style of the viewer
+ */
+ public HyperlinkTokenScanner(IHyperlinkDetector[] hyperlinkDetectors,
+ ISourceViewer viewer, @Nullable TextAttribute defaultAttribute) {
this.hyperlinkDetectors = hyperlinkDetectors;
this.viewer = viewer;
+ this.defaultToken = new Token(defaultAttribute);
}
@Override
@@ -84,8 +106,13 @@ public class HyperlinkTokenScanner implements ITokenScanner {
}
}
}
- currentOffset++;
- return DEFAULT;
+ int actualOffset = currentOffset;
+ IToken token = scanToken();
+ if (token != null && actualOffset < currentOffset) {
+ return token;
+ }
+ currentOffset = actualOffset + 1;
+ return defaultToken;
}
@Override
@@ -116,10 +143,22 @@ public class HyperlinkTokenScanner implements ITokenScanner {
protected void setRangeAndColor(@NonNull IDocument document, int offset,
int length, @Nullable Color color) {
Assert.isTrue(document == viewer.getDocument());
+ this.document = document;
this.endOfRange = offset + length;
this.currentOffset = offset;
this.tokenStart = -1;
this.hyperlinkToken = new Token(
new TextAttribute(color, null, TextAttribute.UNDERLINE));
}
+
+ /**
+ * Invoked if there is no hyperlink at the current position; may check for
+ * additional tokens. If a token is found, must advance currentOffset and
+ * return the token.
+ *
+ * @return the {@link IToken}, or {@code null} if none.
+ */
+ protected IToken scanToken() {
+ return null;
+ }
} \ No newline at end of file
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java
index b259d627be..f991f3d543 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitInfoBuilder.java
@@ -4,6 +4,7 @@
* Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2011, Jens Baumgart <jens.baumgart@sap.com>
* Copyright (C) 2011, Stefan Lay <stefan.lay@sap.com>
+ * Copyright (C) 2015, 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 v1.0
@@ -16,6 +17,7 @@ package org.eclipse.egit.ui.internal.history;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -30,7 +32,9 @@ import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.history.CommitMessageViewer.ObjectLink;
+import org.eclipse.egit.ui.internal.history.FormatJob.FormatResult;
import org.eclipse.egit.ui.internal.trace.GitTraceLocation;
+import org.eclipse.jface.text.Region;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
@@ -44,9 +48,6 @@ import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.RevWalkUtils;
import org.eclipse.osgi.util.NLS;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StyleRange;
-import org.eclipse.swt.graphics.Color;
/**
* Class to build and format commit info in History View
@@ -61,16 +62,15 @@ public class CommitInfoBuilder {
private final DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
+ private static final Pattern FOOTER_PATTERN = Pattern
+ .compile("(?:\n(?:[A-Z](?:[A-Za-z]+-)*[A-Za-z]+:[^\n]*))+$"); //$NON-NLS-1$
+
private PlotCommit<?> commit;
private final Repository db;
private final boolean fill;
- private Color linkColor;
-
- private Color darkGrey;
-
private final Collection<Ref> allRefs;
/**
@@ -88,26 +88,14 @@ public class CommitInfoBuilder {
}
/**
- * set colors for formatting
+ * Retrieves and formats the commit info.
*
- * @param linkColor
- * @param darkGrey
- */
- public void setColors(Color linkColor, Color darkGrey) {
- this.linkColor = linkColor;
- this.darkGrey = darkGrey;
- }
-
- /**
- * Format the commit info
- *
- * @param styles styles for text formatting
* @param monitor
+ * for progress reporting and cancellation
* @return formatted commit info
* @throws IOException
*/
- public String format(final List<StyleRange> styles,
- IProgressMonitor monitor) throws IOException {
+ public FormatResult format(IProgressMonitor monitor) throws IOException {
boolean trace = GitTraceLocation.HISTORYVIEW.isActive();
if (trace)
GitTraceLocation.getTrace().traceEntry(
@@ -116,6 +104,7 @@ public class CommitInfoBuilder {
final StringBuilder d = new StringBuilder();
final PersonIdent author = commit.getAuthorIdent();
final PersonIdent committer = commit.getCommitterIdent();
+ List<ObjectLink> hyperlinks = new ArrayList<>();
d.append(UIText.CommitMessageViewer_commit);
d.append(SPACE);
d.append(commit.getId().name());
@@ -148,7 +137,7 @@ public class CommitInfoBuilder {
p.parseBody();
d.append(UIText.CommitMessageViewer_parent);
d.append(": "); //$NON-NLS-1$
- addLink(d, styles, p);
+ addLink(d, hyperlinks, p);
d.append(" ("); //$NON-NLS-1$
d.append(p.getShortMessage());
d.append(")"); //$NON-NLS-1$
@@ -160,7 +149,7 @@ public class CommitInfoBuilder {
p.parseBody();
d.append(UIText.CommitMessageViewer_child);
d.append(": "); //$NON-NLS-1$
- addLink(d, styles, p);
+ addLink(d, hyperlinks, p);
d.append(" ("); //$NON-NLS-1$
d.append(p.getShortMessage());
d.append(")"); //$NON-NLS-1$
@@ -179,7 +168,7 @@ public class CommitInfoBuilder {
Ref head = i.next();
RevCommit p;
p = rw.parseCommit(head.getObjectId());
- addLink(d, formatHeadRef(head), styles, p);
+ addLink(d, formatHeadRef(head), hyperlinks, p);
if (i.hasNext()) {
if (count++ <= MAXBRANCHES) {
d.append(", "); //$NON-NLS-1$
@@ -214,7 +203,7 @@ public class CommitInfoBuilder {
d.append(": "); //$NON-NLS-1$
RevCommit p = rw.parseCommit(followingTag
.getObjectId());
- addLink(d, formatTagRef(followingTag), styles, p);
+ addLink(d, formatTagRef(followingTag), hyperlinks, p);
d.append(LF);
}
} catch (IOException e) {
@@ -229,7 +218,7 @@ public class CommitInfoBuilder {
d.append(": "); //$NON-NLS-1$
RevCommit p = rw.parseCommit(precedingTag
.getObjectId());
- addLink(d, formatTagRef(precedingTag), styles, p);
+ addLink(d, formatTagRef(precedingTag), hyperlinks, p);
d.append(LF);
}
} catch (IOException e) {
@@ -237,50 +226,47 @@ public class CommitInfoBuilder {
}
}
- makeGrayText(d, styles);
d.append(LF);
- String msg = commit.getFullMessage();
- Pattern p = Pattern.compile("\n([A-Z](?:[A-Za-z]+-)+by: [^\n]+)"); //$NON-NLS-1$
- if (fill) {
- Matcher spm = p.matcher(msg);
- if (spm.find()) {
- String subMsg = msg.substring(0, spm.end());
- msg = subMsg.replaceAll("([\\w.,; \t])\n(\\w)", "$1 $2") //$NON-NLS-1$ //$NON-NLS-2$
- + msg.substring(spm.end());
+ int headerEnd = d.length();
+ String msg = commit.getFullMessage().trim();
+ // Find start of footer:
+ Matcher spm = FOOTER_PATTERN.matcher(msg);
+ int footerStart = -1;
+ if (spm.find()) {
+ if (fill) {
+ String footer = msg.substring(spm.start());
+ msg = msg.substring(0, spm.start());
+ msg = msg.replaceAll("([\\w.,; \t])\n(\\w)", "$1 $2") //$NON-NLS-1$ //$NON-NLS-2$
+ + footer;
+ footerStart = headerEnd + msg.length() - footer.length();
+ } else {
+ footerStart = headerEnd + spm.start();
}
+ } else if (fill) {
+ msg = msg.replaceAll("([\\w.,; \t])\n(\\w)", "$1 $2"); //$NON-NLS-1$ //$NON-NLS-2$
}
- int h0 = d.length();
+
d.append(msg);
if (!msg.endsWith(LF))
d.append(LF);
- Matcher matcher = p.matcher(msg);
- while (matcher.find()) {
- styles.add(new StyleRange(h0 + matcher.start(), matcher.end()
- - matcher.start(), null, null, SWT.ITALIC));
- }
-
if (trace)
GitTraceLocation.getTrace().traceExit(
GitTraceLocation.HISTORYVIEW.getLocation());
- return d.toString();
+ return new FormatResult(d.toString(), hyperlinks, headerEnd,
+ footerStart >= 0 ? footerStart : d.length());
}
- private void addLink(final StringBuilder d, String linkLabel,
- final List<StyleRange> styles, final RevCommit to) {
- final ObjectLink sr = new ObjectLink();
- sr.targetCommit = to;
- sr.foreground = linkColor;
- sr.underline = true;
- sr.start = d.length();
+ private void addLink(StringBuilder d, String linkLabel,
+ Collection<ObjectLink> hyperlinks, RevCommit to) {
+ hyperlinks.add(
+ new ObjectLink(to, new Region(d.length(), linkLabel.length())));
d.append(linkLabel);
- sr.length = d.length() - sr.start;
- styles.add(sr);
}
- private void addLink(final StringBuilder d, final List<StyleRange> styles,
- final RevCommit to) {
- addLink(d, to.getId().name(), styles, to);
+ private void addLink(StringBuilder d, Collection<ObjectLink> hyperlinks,
+ RevCommit to) {
+ addLink(d, to.getId().name(), hyperlinks, to);
}
/**
@@ -318,28 +304,6 @@ public class CommitInfoBuilder {
return name;
}
- private void makeGrayText(StringBuilder d, List<StyleRange> styles) {
- int p0 = 0;
- for (int i = 0; i < styles.size(); ++i) {
- StyleRange r = styles.get(i);
- if (p0 < r.start) {
- StyleRange nr = new StyleRange(p0, r.start - p0, darkGrey,
- null);
- styles.add(i, nr);
- p0 = r.start;
- } else {
- if (r.foreground == null)
- r.foreground = darkGrey;
- p0 = r.start + r.length;
- }
- }
- if (d.length() - 1 > p0) {
- StyleRange nr = new StyleRange(p0, d.length() - p0, darkGrey,
- null);
- styles.add(nr);
- }
- }
-
private String getTagsString() {
StringBuilder sb = new StringBuilder();
Map<String, Ref> tagsMap = db.getTags();
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java
index de3769d81e..fe4d7ea412 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitMessageViewer.java
@@ -6,6 +6,7 @@
* Copyright (C) 2011, Stefan Lay <stefan.lay@sap.com>
* Copyright (C) 2014, Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
* Copyright (C) 2015, IBM Corporation (Dani Megert <daniel_megert@ch.ibm.com>)
+ * Copyright (C) 2015, 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 v1.0
@@ -34,10 +35,21 @@ import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.preference.IPersistentPreferenceStore;
import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.text.DefaultTextDoubleClickStrategy;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.jface.text.rules.FastPartitioner;
+import org.eclipse.jface.text.rules.IPartitionTokenScanner;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
@@ -51,37 +63,21 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revplot.PlotCommit;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IWorkbenchPartSite;
-import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
class CommitMessageViewer extends SourceViewer {
- private static final Color SYS_LINKCOLOR = PlatformUI.getWorkbench()
- .getDisplay().getSystemColor(SWT.COLOR_BLUE);
+ static final String HEADER_CONTENT_TYPE = "__egit_commit_msg_header"; //$NON-NLS-1$
- private static final Color SYS_DARKGRAY = PlatformUI.getWorkbench()
- .getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
-
- private static final Cursor SYS_LINK_CURSOR = PlatformUI.getWorkbench()
- .getDisplay().getSystemCursor(SWT.CURSOR_HAND);
-
- private final Cursor sys_normalCursor;
+ static final String FOOTER_CONTENT_TYPE = "__egit_commit_msg_footer"; //$NON-NLS-1$
// notified when clicking on a link in the message (branch, commit...)
private final ListenerList navListeners = new ListenerList();
@@ -89,6 +85,9 @@ class CommitMessageViewer extends SourceViewer {
// listener to detect changes in the wrap and fill preferences
private final IPropertyChangeListener listener;
+ // Listener to react on syntax coloring preferences changes
+ private final IPropertyChangeListener syntaxColoringListener;
+
// the current repository
private Repository db;
@@ -121,35 +120,6 @@ class CommitMessageViewer extends SourceViewer {
final StyledText t = getTextWidget();
t.setFont(UIUtils.getFont(UIPreferences.THEME_CommitMessageFont));
- sys_normalCursor = t.getCursor();
-
- // set the cursor when hovering over a link
- t.addListener(SWT.MouseMove, new Listener() {
- @Override
- public void handleEvent(final Event e) {
- StyleRange styleRange = getStyleRange(e.x, e.y);
- if (styleRange != null && styleRange.underline)
- t.setCursor(SYS_LINK_CURSOR);
- else
- t.setCursor(sys_normalCursor);
- }
- });
- // react on link click
- t.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseDown(final MouseEvent e) {
- // only process the hyper link if it was a primary mouse click
- if (e.button != 1)
- return;
-
- final StyleRange r = getStyleRange(e.x, e.y);
- if (r instanceof ObjectLink) {
- final RevCommit c = ((ObjectLink) r).targetCommit;
- for (final Object l : navListeners.getListeners())
- ((CommitNavigationListener) l).showCommit(c);
- }
- }
- });
setTextDoubleClickStrategy(new DefaultTextDoubleClickStrategy(),
IDocument.DEFAULT_CONTENT_TYPE);
activatePlugins();
@@ -172,12 +142,24 @@ class CommitMessageViewer extends SourceViewer {
}
}
};
-
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
store.addPropertyChangeListener(listener);
fill = store
.getBoolean(UIPreferences.RESOURCEHISTORY_SHOW_COMMENT_FILL);
+ // React on changes in the JFace color preferences
+ syntaxColoringListener = new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (JFacePreferences.HYPERLINK_COLOR
+ .equals(event.getProperty())) {
+ format();
+ }
+ }
+ };
+ JFacePreferences.getPreferenceStore()
+ .addPropertyChangeListener(syntaxColoringListener);
+
// global action handlers for select all and copy
final IAction selectAll = new Action() {
@Override
@@ -300,6 +282,8 @@ class CommitMessageViewer extends SourceViewer {
}
Activator.getDefault().getPreferenceStore()
.removePropertyChangeListener(listener);
+ JFacePreferences.getPreferenceStore()
+ .removePropertyChangeListener(syntaxColoringListener);
if (refsChangedListener != null)
refsChangedListener.remove();
refsChangedListener = null;
@@ -387,8 +371,7 @@ class CommitMessageViewer extends SourceViewer {
if (siteService == null)
return;
FormatJob.FormatRequest formatRequest = new FormatJob.FormatRequest(
- getRepository(), commit, fill, SYS_LINKCOLOR, SYS_DARKGRAY,
- allRefs);
+ getRepository(), commit, fill, allRefs);
formatJob = new FormatJob(formatRequest);
addDoneListenerToFormatJob();
siteService.schedule(formatJob, 0 /* now */, true /*
@@ -402,68 +385,196 @@ class CommitMessageViewer extends SourceViewer {
if (!UIUtils.isUsable(text))
return;
- setDocument(new Document(formatResult.getCommitInfo()));
+ setDocument(new CommitDocument(formatResult));
+ }
+
+ static class ObjectLink {
+ private final RevCommit target;
+ private final IRegion region;
+
+ public ObjectLink(RevCommit target, IRegion region) {
+ this.target = target;
+ this.region = region;
+ }
+
+ public IRegion getRegion() {
+ return region;
+ }
- // Set style ranges from format job. We know that they are already
- // ordered and don't overlap.
- text.setStyleRanges(formatResult.getStyleRange());
+ public RevCommit getTarget() {
+ return target;
+ }
- // Apply additional styles. If we combined them with the above style
- // ranges and set them all at once, we would have to manually remove
- // overlapping ones.
- UIUtils.applyHyperlinkDetectorStyleRanges(CommitMessageViewer.this,
- fHyperlinkDetectors);
}
- static final class ObjectLink extends StyleRange {
- RevCommit targetCommit;
+ private class ObjectHyperlink implements IHyperlink {
+
+ ObjectLink link;
+
+ public ObjectHyperlink(ObjectLink link) {
+ this.link = link;
+ }
+
+ @Override
+ public IRegion getHyperlinkRegion() {
+ return link.getRegion();
+ }
@Override
- public boolean similarTo(final StyleRange style) {
- if (!(style instanceof ObjectLink))
- return false;
- if (targetCommit != ((ObjectLink) style).targetCommit)
- return false;
- return super.similarTo(style);
+ public String getTypeLabel() {
+ return null;
}
@Override
- public boolean equals(Object object) {
- return super.equals(object)
- && targetCommit.equals(((ObjectLink) object).targetCommit);
+ public String getHyperlinkText() {
+ return link.getTarget().name();
}
@Override
- public int hashCode() {
- return super.hashCode() ^ targetCommit.hashCode();
+ public void open() {
+ for (final Object l : navListeners.getListeners())
+ ((CommitNavigationListener) l).showCommit(link.getTarget());
}
+
}
- private void setFill(boolean fill) {
- this.fill = fill;
- format();
+ private class CommitDocument extends Document {
+
+ private final List<IHyperlink> hyperlinks;
+
+ private final int headerEnd;
+
+ private final int footerStart;
+
+ public CommitDocument(FormatResult format) {
+ super(format.getCommitInfo());
+ headerEnd = format.getHeaderEnd();
+ footerStart = format.getFooterStart();
+ List<ObjectLink> knownLinks = format.getKnownLinks();
+ hyperlinks = new ArrayList<>(knownLinks.size());
+ for (ObjectLink o : knownLinks) {
+ hyperlinks.add(new ObjectHyperlink(o));
+ }
+ IDocumentPartitioner partitioner = new FastPartitioner(
+ new CommitPartitionTokenScanner(),
+ new String[] { IDocument.DEFAULT_CONTENT_TYPE,
+ HEADER_CONTENT_TYPE, FOOTER_CONTENT_TYPE });
+ partitioner.connect(this);
+ this.setDocumentPartitioner(partitioner);
+ }
+
+ public List<IHyperlink> getKnownHyperlinks() {
+ return hyperlinks;
+ }
+
+ public int getHeaderEnd() {
+ return headerEnd;
+ }
+
+ public int getFooterStart() {
+ return footerStart;
+ }
}
- /**
- * Get style range at x/y coordinates
- *
- * @param x
- * @param y
- * @return style range, will be null when no style range exists at given
- * coordinates
- */
- private StyleRange getStyleRange(final int x, final int y) {
- final StyledText t = getTextWidget();
- final int offset;
- try {
- offset = t.getOffsetAtLocation(new Point(x, y));
- } catch (IllegalArgumentException e) {
- return null;
+ private static class CommitPartitionTokenScanner
+ implements IPartitionTokenScanner {
+
+ private static final IToken HEADER = new Token(HEADER_CONTENT_TYPE);
+
+ private static final IToken BODY = new Token(
+ IDocument.DEFAULT_CONTENT_TYPE);
+
+ private static final IToken FOOTER = new Token(FOOTER_CONTENT_TYPE);
+
+ private int headerEnd;
+
+ private int footerStart;
+
+ private int currentOffset;
+
+ private int end;
+
+ private int tokenStart;
+
+ @Override
+ public void setRange(IDocument document, int offset, int length) {
+ if (document instanceof CommitDocument) {
+ CommitDocument d = (CommitDocument) document;
+ headerEnd = d.getHeaderEnd();
+ footerStart = d.getFooterStart();
+ } else {
+ headerEnd = 0;
+ footerStart = document.getLength();
+ }
+ currentOffset = offset;
+ end = offset + length;
+ tokenStart = -1;
+ }
+
+ @Override
+ public IToken nextToken() {
+ if (currentOffset < end) {
+ tokenStart = currentOffset;
+ if (currentOffset < headerEnd) {
+ currentOffset = Math.min(headerEnd, end);
+ return HEADER;
+ } else if (currentOffset < footerStart) {
+ currentOffset = Math.min(footerStart, end);
+ return BODY;
+ } else {
+ currentOffset = end;
+ return FOOTER;
+ }
+ }
+ return Token.EOF;
+ }
+
+ @Override
+ public int getTokenOffset() {
+ return tokenStart;
+ }
+
+ @Override
+ public int getTokenLength() {
+ return currentOffset - tokenStart;
+ }
+
+ @Override
+ public void setPartialRange(IDocument document, int offset, int length,
+ String contentType, int partitionOffset) {
+ setRange(document, offset, length);
}
- if (offset < t.getCharCount())
- return t.getStyleRangeAtOffset(offset);
- else
+
+ }
+
+ static class KnownHyperlinksDetector implements IHyperlinkDetector {
+
+ @Override
+ public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
+ IRegion region, boolean canShowMultipleHyperlinks) {
+ IDocument document = textViewer.getDocument();
+ if (document instanceof CommitDocument) {
+ List<IHyperlink> knownLinks = ((CommitDocument) document)
+ .getKnownHyperlinks();
+ List<IHyperlink> result = new ArrayList<>();
+ for (IHyperlink link : knownLinks) {
+ IRegion linkRegion = link.getHyperlinkRegion();
+ if (TextUtilities.overlaps(linkRegion, region)) {
+ result.add(link);
+ }
+ }
+ if (!result.isEmpty()) {
+ return result.toArray(new IHyperlink[result.size()]);
+ }
+ }
return null;
+ }
+
+ }
+
+ private void setFill(boolean fill) {
+ this.fill = fill;
+ format();
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FormatJob.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FormatJob.java
index b12d38416f..b93b1a8924 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FormatJob.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/FormatJob.java
@@ -1,6 +1,7 @@
/*******************************************************************************
* Copyright (C) 2011, Jens Baumgart <jens.baumgart@sap.com>
* Copyright (C) 2011, Stefan Lay <stefan.lay@sap.com>
+ * Copyright (C) 2015, 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 v1.0
@@ -10,10 +11,7 @@
package org.eclipse.egit.ui.internal.history;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -23,11 +21,10 @@ import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.history.CommitMessageViewer.ObjectLink;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revplot.PlotCommit;
-import org.eclipse.swt.custom.StyleRange;
-import org.eclipse.swt.graphics.Color;
class FormatJob extends Job {
@@ -57,8 +54,7 @@ class FormatJob extends Job {
protected IStatus run(IProgressMonitor monitor) {
if(monitor.isCanceled())
return Status.CANCEL_STATUS;
- final List<StyleRange> styles = new ArrayList<StyleRange>();
- final String commitInfo;
+ final FormatResult commitInfo;
CommitInfoBuilder builder;
try {
synchronized(lock) {
@@ -67,47 +63,21 @@ class FormatJob extends Job {
builder = new CommitInfoBuilder(formatRequest.getRepository(),
commit, formatRequest.isFill(),
formatRequest.getAllRefs());
- builder.setColors(formatRequest.getLinkColor(),
- formatRequest.getDarkGrey());
}
- commitInfo = builder.format(styles, monitor);
+ commitInfo = builder.format(monitor);
} catch (IOException e) {
return Activator.createErrorStatus(e.getMessage(), e);
}
- final StyleRange[] arr = new StyleRange[styles.size()];
- styles.toArray(arr);
- Arrays.sort(arr, new Comparator<StyleRange>() {
- @Override
- public int compare(StyleRange o1, StyleRange o2) {
- return o1.start - o2.start;
- }
- });
if(monitor.isCanceled())
return Status.CANCEL_STATUS;
synchronized(lock) {
- formatResult = new FormatResult(commitInfo, arr);
+ formatResult = commitInfo;
}
return Status.OK_STATUS;
}
static class FormatRequest {
- public Color getLinkColor() {
- return linkColor;
- }
-
- public void setLinkColor(Color linkColor) {
- this.linkColor = linkColor;
- }
-
- public Color getDarkGrey() {
- return darkGrey;
- }
-
- public void setDarkGrey(Color darkGrey) {
- this.darkGrey = darkGrey;
- }
-
public Collection<Ref> getAllRefs() {
return allRefs;
}
@@ -122,20 +92,13 @@ class FormatJob extends Job {
private boolean fill;
- private Color linkColor;
-
- private Color darkGrey;
-
private Collection<Ref> allRefs;
- FormatRequest(Repository repository, PlotCommit<?> commit,
- boolean fill, Color linkColor, Color darkGrey,
+ FormatRequest(Repository repository, PlotCommit<?> commit, boolean fill,
Collection<Ref> allRefs) {
this.repository = repository;
this.commit = commit;
this.fill = fill;
- this.linkColor = linkColor;
- this.darkGrey = darkGrey;
this.allRefs = allRefs;
}
@@ -154,20 +117,36 @@ class FormatJob extends Job {
}
static class FormatResult{
- String commitInfo;
- StyleRange[] styleRange;
+ private final String commitInfo;
+
+ private final List<ObjectLink> knownLinks;
+
+ private final int headerEnd;
+
+ private final int footerStart;
- FormatResult(String commmitInfo, StyleRange[] styleRange) {
+ FormatResult(String commmitInfo, List<ObjectLink> links,
+ int headerEnd, int footerStart) {
this.commitInfo = commmitInfo;
- this.styleRange = styleRange;
+ this.knownLinks = links;
+ this.headerEnd = headerEnd;
+ this.footerStart = footerStart;
}
public String getCommitInfo() {
return commitInfo;
}
- public StyleRange[] getStyleRange() {
- return styleRange;
+ public List<ObjectLink> getKnownLinks() {
+ return knownLinks;
+ }
+
+ public int getHeaderEnd() {
+ return headerEnd;
+ }
+
+ public int getFooterStart() {
+ return footerStart;
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java
index a553da0d65..9ca47acbab 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java
@@ -8,6 +8,7 @@
* Copyright (C) 2012-2013 Robin Stocker <robin@nibor.org>
* Copyright (C) 2012, François Rey <eclipse.org_@_francois_._rey_._name>
* Copyright (C) 2015, IBM Corporation (Dani Megert <daniel_megert@ch.ibm.com>)
+ * Copyright (C) 2015, 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 v1.0
@@ -23,6 +24,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
@@ -46,6 +49,7 @@ import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter;
import org.eclipse.egit.ui.internal.commit.DiffViewer;
+import org.eclipse.egit.ui.internal.dialogs.HyperlinkTokenScanner;
import org.eclipse.egit.ui.internal.repository.tree.AdditionalRefNode;
import org.eclipse.egit.ui.internal.repository.tree.FileNode;
import org.eclipse.egit.ui.internal.repository.tree.FolderNode;
@@ -69,13 +73,19 @@ import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
-import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
-import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.presentation.PresentationReconciler;
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
@@ -902,23 +912,62 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener,
}
@Override
- public IHyperlinkPresenter getHyperlinkPresenter(
+ public IHyperlinkDetector[] getHyperlinkDetectors(
ISourceViewer sourceViewer) {
- return new MultipleHyperlinkPresenter(PlatformUI.getWorkbench()
- .getDisplay().getSystemColor(SWT.COLOR_BLUE).getRGB()) {
-
- @Override
- public void hideHyperlinks() {
- // We want links to always show.
- }
+ IHyperlinkDetector[] registered = getRegisteredHyperlinkDetectors(
+ sourceViewer);
+ // Add our special detector for commit hyperlinks.
+ IHyperlinkDetector[] result = new IHyperlinkDetector[registered.length
+ + 1];
+ System.arraycopy(registered, 0, result, 0, registered.length);
+ result[registered.length] = new CommitMessageViewer.KnownHyperlinksDetector();
+ return result;
+ }
- };
+ @Override
+ public String[] getConfiguredContentTypes(
+ ISourceViewer sourceViewer) {
+ return new String[] { IDocument.DEFAULT_CONTENT_TYPE,
+ CommitMessageViewer.HEADER_CONTENT_TYPE,
+ CommitMessageViewer.FOOTER_CONTENT_TYPE };
}
@Override
- public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
- return getRegisteredHyperlinkDetectors(sourceViewer);
+ public IPresentationReconciler getPresentationReconciler(
+ ISourceViewer viewer) {
+ PresentationReconciler reconciler = new PresentationReconciler();
+ reconciler.setDocumentPartitioning(
+ getConfiguredDocumentPartitioning(commentViewer));
+ DefaultDamagerRepairer hyperlinkDamagerRepairer = new DefaultDamagerRepairer(
+ new HyperlinkTokenScanner(
+ getHyperlinkDetectors(commentViewer),
+ commentViewer));
+ reconciler.setDamager(hyperlinkDamagerRepairer,
+ IDocument.DEFAULT_CONTENT_TYPE);
+ reconciler.setRepairer(hyperlinkDamagerRepairer,
+ IDocument.DEFAULT_CONTENT_TYPE);
+ TextAttribute headerDefault = new TextAttribute(
+ PlatformUI.getWorkbench().getDisplay()
+ .getSystemColor(SWT.COLOR_DARK_GRAY));
+ DefaultDamagerRepairer headerDamagerRepairer = new DefaultDamagerRepairer(
+ new HyperlinkTokenScanner(
+ getHyperlinkDetectors(commentViewer),
+ commentViewer, headerDefault));
+ reconciler.setDamager(headerDamagerRepairer,
+ CommitMessageViewer.HEADER_CONTENT_TYPE);
+ reconciler.setRepairer(headerDamagerRepairer,
+ CommitMessageViewer.HEADER_CONTENT_TYPE);
+ DefaultDamagerRepairer footerDamagerRepairer = new DefaultDamagerRepairer(
+ new FooterTokenScanner(
+ getHyperlinkDetectors(commentViewer),
+ commentViewer));
+ reconciler.setDamager(footerDamagerRepairer,
+ CommitMessageViewer.FOOTER_CONTENT_TYPE);
+ reconciler.setRepairer(footerDamagerRepairer,
+ CommitMessageViewer.FOOTER_CONTENT_TYPE);
+ return reconciler;
}
+
};
commentViewer.configure(configuration);
@@ -2346,4 +2395,52 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener,
public String getRenamedPath(String path, ObjectId commit) {
return renameTracker.getPath(commit, path);
}
+
+ private static class FooterTokenScanner extends HyperlinkTokenScanner {
+
+ private static final Pattern ITALIC_LINE = Pattern
+ .compile("^[A-Z](?:[A-Za-z]+-)+by: "); //$NON-NLS-1$
+
+ private final IToken italicToken;
+
+ public FooterTokenScanner(IHyperlinkDetector[] hyperlinkDetectors,
+ ISourceViewer viewer) {
+ super(hyperlinkDetectors, viewer);
+ Object defaults = defaultToken.getData();
+ TextAttribute italic;
+ if (defaults instanceof TextAttribute) {
+ TextAttribute defaultAttribute = (TextAttribute) defaults;
+ int style = defaultAttribute.getStyle() ^ SWT.ITALIC;
+ italic = new TextAttribute(defaultAttribute.getForeground(),
+ defaultAttribute.getBackground(), style,
+ defaultAttribute.getFont());
+ } else {
+ italic = new TextAttribute(null, null, SWT.ITALIC);
+ }
+ italicToken = new Token(italic);
+ }
+
+ @Override
+ protected IToken scanToken() {
+ // If we're at a "Signed-off-by" or similar footer line, make it
+ // italic.
+ try {
+ IRegion region = document
+ .getLineInformationOfOffset(currentOffset);
+ if (currentOffset == region.getOffset()) {
+ String line = document.get(currentOffset,
+ region.getLength());
+ Matcher m = ITALIC_LINE.matcher(line);
+ if (m.find()) {
+ currentOffset = Math.min(endOfRange,
+ currentOffset + region.getLength());
+ return italicToken;
+ }
+ }
+ } catch (BadLocationException e) {
+ // Ignore and return null below.
+ }
+ return null;
+ }
+ }
}

Back to the top