Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf2016-11-25 19:28:04 +0000
committerMatthias Sohn2016-12-03 23:44:48 +0000
commitdf38739c2961914202372451ffc5196d0991afc7 (patch)
treebedf8de47f967fc3865815dc1988fe8da261095a
parent6d01c2caccaa282fc700599bb48e3cbf2bc1edc9 (diff)
downloadegit-df38739c2961914202372451ffc5196d0991afc7.tar.gz
egit-df38739c2961914202372451ffc5196d0991afc7.tar.xz
egit-df38739c2961914202372451ffc5196d0991afc7.zip
Add hyperlinks to DiffViewer
Provide links on file headlines and hunk headers to open files in editors. Provides links to: * Open the previous version in an editor * Open the workspace version (if it exists) in an editor * Open "this" version (i.e., the “current” one of the diff) in an editor * Open a two-way diff between the previous and this version in a compare editor Hyperlinks were chosen instead of a context menu because: * A context menu sensitive to the location in the text would have needed to be based on the location of the current selection, which is cumbersome. * A context menu based on click coordinates is not a good idea because * one has no immediate access to the coordinates of the mouse event triggering the context menu (and if one had, it would need to be converted to a text location), and * when the context menu is triggered via the keyboard (Shift-F10), there is no reasonable coordinate to use anyway. Add a hyperlink detector to create links to open files (and reveal the affected lines if on a hunk). As it turned out, simply adding a hyperlink detector worked already, but had a few problems: * Background and foreground colors got messed up when mousing over the hyperlinks because the JFace hyperlink infrastructure didn’t interact well with the purely SWT-based coloring done in the viewer. * The hyperlink pop-up giving access to the different open "commands" when there are multiple hyperlinks at the same location took longer to appear the further down in the diff the hyperlink was. No idea why; the hyperlink detector itself is fast. Therefore rewrite the DiffViewer to use the JFace infrastructure for syntax coloring. The hand-crafted SWT coloring is a dead-end and makes adding new features very hard. Added a document partitioner, plus a presentation reconciler. The line background listener was kept to get backgrounds extending over the whole width of the viewer (syntax coloring alone would only set the background on actual content). This solves the color problems, and interestingly also makes the hyperlink pop-ups appear speedily, even at the bottom of a lengthy diff. Also move all the "open file (version)" methods to DiffViewer and use them in StagingView, CommitFileDiffViewer, and CompareTreeView instead of duplicating code all around. Change-Id: I84332f8e07d9f9928a66a91799dec7650de6b44c Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java48
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java24
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/blame/BlameInformationControl.java21
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffDocument.java242
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffEditorPage.java9
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java102
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffViewer.java786
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java28
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkSourceViewer.java16
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitFileDiffViewer.java229
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/GitHistoryPage.java8
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/GitCompareFileRevisionEditorInput.java23
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java23
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties8
14 files changed, 1217 insertions, 350 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java
index 10e3801ada..8476b9ab16 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java
@@ -28,8 +28,10 @@ import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.revision.FileRevisionEditorInput;
-import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.util.OpenStrategy;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.ui.IEditorDescriptor;
@@ -40,6 +42,8 @@ import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.texteditor.ITextEditor;
/**
* Taken from the Team UI plug in Utils class
@@ -219,4 +223,46 @@ public class EgitUiEditorUtils {
}
return type;
}
+
+ /**
+ * Reveals the given {@code lineNo} if it is greater than zero and the
+ * editor is an {@link ITextEditor}.
+ *
+ * @param editor
+ * to reveal the line in
+ * @param lineNo
+ * to reveal
+ */
+ public static void revealLine(IEditorPart editor, int lineNo) {
+ if (lineNo < 0) {
+ return;
+ }
+ ITextEditor textEditor = getTextEditor(editor);
+ if (textEditor == null) {
+ return;
+ }
+ IDocument document = textEditor.getDocumentProvider()
+ .getDocument(textEditor.getEditorInput());
+ if (document == null) {
+ return;
+ }
+ try {
+ textEditor.selectAndReveal(document.getLineOffset(lineNo), 0);
+ } catch (BadLocationException e) {
+ // Ignore
+ }
+ }
+
+ private static ITextEditor getTextEditor(IEditorPart editor) {
+ if (editor instanceof ITextEditor) {
+ return (ITextEditor) editor;
+ } else if (editor instanceof MultiPageEditorPart) {
+ Object nestedEditor = ((MultiPageEditorPart) editor)
+ .getSelectedPage();
+ if (nestedEditor instanceof ITextEditor) {
+ return (ITextEditor) nestedEditor;
+ }
+ }
+ return null;
+ }
}
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 b5b373d906..fe4fadc4da 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
@@ -4341,9 +4341,6 @@ public class UIText extends NLS {
public static String CommitFileDiffViewer_CompareWorkingDirectoryMenuLabel;
/** */
- public static String CommitFileDiffViewer_FileDoesNotExist;
-
- /** */
public static String CommitFileDiffViewer_MergeCommitMultiAncestorMessage;
/** */
@@ -4353,15 +4350,30 @@ public class UIText extends NLS {
public static String CommitFileDiffViewer_OpenPreviousInEditorMenuLabel;
/** */
- public static String CommitFileDiffViewer_notContainedInCommit;
-
- /** */
public static String CommitFileDiffViewer_ShowAnnotationsMenuLabel;
/** */
public static String CommitFileDiffViewer_ShowInHistoryLabel;
/** */
+ public static String DiffViewer_FileDoesNotExist;
+
+ /** */
+ public static String DiffViewer_OpenComparisonLinkLabel;
+
+ /** */
+ public static String DiffViewer_OpenWorkingTreeLinkLabel;
+
+ /** */
+ public static String DiffViewer_OpenInEditorLinkLabel;
+
+ /** */
+ public static String DiffViewer_OpenPreviousLinkLabel;
+
+ /** */
+ public static String DiffViewer_notContainedInCommit;
+
+ /** */
public static String CommitGraphTable_CommitId;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/blame/BlameInformationControl.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/blame/BlameInformationControl.java
index ad6411878e..c1d1413c78 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/blame/BlameInformationControl.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/blame/BlameInformationControl.java
@@ -16,6 +16,7 @@ import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
+import org.eclipse.egit.core.internal.CompareCoreUtils;
import org.eclipse.egit.core.internal.job.JobUtil;
import org.eclipse.egit.core.internal.storage.CommitFileRevision;
import org.eclipse.egit.ui.Activator;
@@ -28,22 +29,24 @@ import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.blame.BlameOperation.BlameHistoryPageInput;
import org.eclipse.egit.ui.internal.blame.BlameRevision.Diff;
import org.eclipse.egit.ui.internal.commit.CommitEditor;
+import org.eclipse.egit.ui.internal.commit.DiffDocument;
import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter;
import org.eclipse.egit.ui.internal.commit.DiffViewer;
import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
+import org.eclipse.egit.ui.internal.history.FileDiff;
import org.eclipse.egit.ui.internal.history.HistoryPageInput;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.AbstractInformationControl;
-import org.eclipse.jface.text.Document;
-import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.EditList;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -370,7 +373,7 @@ public class BlameInformationControl extends AbstractInformationControl
diffText.getControl().setLayoutData(
GridDataFactory.fillDefaults().grab(true, true).create());
- IDocument document = new Document();
+ DiffDocument document = new DiffDocument();
DiffStyleRangeFormatter diffFormatter = new DiffStyleRangeFormatter(
document);
diffFormatter.setContext(1);
@@ -378,8 +381,18 @@ public class BlameInformationControl extends AbstractInformationControl
diffFormatter.format(interestingDiff, diff.getOldText(),
diff.getNewText());
+ try (ObjectReader reader = revision.getRepository().newObjectReader()) {
+ DiffEntry diffEntry = CompareCoreUtils.getChangeDiffEntry(
+ revision.getRepository(), revision.getSourcePath(),
+ revision.getCommit(), parent, reader);
+ if (diffEntry != null) {
+ FileDiff fileDiff = new FileDiff(revision.getCommit(),
+ diffEntry);
+ document.setDefault(revision.getRepository(), fileDiff);
+ }
+ }
+ document.connect(diffFormatter);
diffText.setDocument(document);
- diffText.setFormatter(diffFormatter);
}
private EditList getInterestingDiff(EditList fullDiff) {
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffDocument.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffDocument.java
new file mode 100644
index 0000000000..302973b2ee
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffDocument.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (C) 2016, 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.commit;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter.DiffStyleRange;
+import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter.FileDiffRange;
+import org.eclipse.egit.ui.internal.history.FileDiff;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+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.jgit.diff.DiffEntry;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * A {@link Document} specialized for displaying unified diffs generated by a
+ * {@link DiffStyleRangeFormatter}. Intended usage is to create a DiffDocument,
+ * let a DiffStyleRangeFormatter generate into it, and then
+ * {@link #connect(DiffStyleRangeFormatter) connect()} the formatter. This will
+ * partition the document into regions for file headlines, hunks, and added or
+ * removed lines.
+ */
+public class DiffDocument extends Document {
+
+ static final String HEADLINE_CONTENT_TYPE = "_egit_diff_headline"; //$NON-NLS-1$
+
+ static final String HUNK_CONTENT_TYPE = "_egit_diff_hunk"; //$NON-NLS-1$
+
+ static final String ADDED_CONTENT_TYPE = "_egit_diff_added"; //$NON-NLS-1$
+
+ static final String REMOVED_CONTENT_TYPE = "_egit_diff_removed"; //$NON-NLS-1$
+
+ private DiffStyleRange[] ranges;
+
+ private FileDiffRange[] fileRanges;
+
+ private Pattern newPathPattern;
+
+ private Pattern oldPathPattern;
+
+ private Repository defaultRepository;
+
+ private FileDiff defaultFileDiff;
+
+ /**
+ * Creates a new {@link DiffDocument}.
+ */
+ public DiffDocument() {
+ super();
+ }
+
+ /**
+ * Creates a new {@link DiffDocument} with initial text.
+ *
+ * @param text
+ * to set on the document
+ */
+ public DiffDocument(String text) {
+ super(text);
+ }
+
+ /**
+ * Sets up the document to use information from the given
+ * {@link DiffStyleRangeFormatter} for partitioning the document into
+ * partitions for file headlines, hunk headers, and added or removed lines.
+ * It is assumed that the given formatter has been used to generate content
+ * into the document.
+ *
+ * @param formatter
+ * to obtain information from
+ */
+ public void connect(DiffStyleRangeFormatter formatter) {
+ ranges = formatter.getRanges();
+ fileRanges = formatter.getFileRanges();
+ if ((fileRanges == null || fileRanges.length == 0)
+ && defaultRepository != null && defaultFileDiff != null) {
+ fileRanges = new FileDiffRange[] { new FileDiffRange(
+ defaultRepository, defaultFileDiff, 0, getLength()) };
+ }
+ newPathPattern = Pattern.compile(
+ Pattern.quote(formatter.getNewPrefix()) + "\\S+"); //$NON-NLS-1$
+ oldPathPattern = Pattern.compile(
+ Pattern.quote(formatter.getOldPrefix()) + "\\S+"); //$NON-NLS-1$
+ // Connect a new partitioner.
+ IDocumentPartitioner partitioner = new FastPartitioner(
+ new DiffPartitionTokenScanner(),
+ new String[] { IDocument.DEFAULT_CONTENT_TYPE,
+ HEADLINE_CONTENT_TYPE, HUNK_CONTENT_TYPE,
+ ADDED_CONTENT_TYPE, REMOVED_CONTENT_TYPE });
+ IDocumentPartitioner oldPartitioner = getDocumentPartitioner();
+ if (oldPartitioner != null) {
+ oldPartitioner.disconnect();
+ }
+ partitioner.connect(this);
+ setDocumentPartitioner(partitioner);
+ }
+
+ /**
+ * Provide default settings about the {@link Repository} and
+ * {@link FileDiff}, to be used in the absence of explicit information from
+ * a connected {@link DiffStyleRangeFormatter}. Useful if the document is
+ * used for only individual edits from a file.
+ *
+ * @param repository
+ * to use if none set explicitly
+ * @param fileDiff
+ * to use if none set explicitly
+ */
+ public void setDefault(Repository repository, FileDiff fileDiff) {
+ defaultRepository = repository;
+ defaultFileDiff = fileDiff;
+ }
+
+ DiffStyleRange[] getRanges() {
+ return ranges;
+ }
+
+ FileDiffRange[] getFileRanges() {
+ return fileRanges;
+ }
+
+ Pattern getPathPattern(DiffEntry.Side side) {
+ switch (side) {
+ case OLD:
+ return oldPathPattern;
+ default:
+ return newPathPattern;
+ }
+ }
+
+ private class DiffPartitionTokenScanner implements IPartitionTokenScanner {
+
+ private final Token HEADLINE_TOKEN = new Token(HEADLINE_CONTENT_TYPE);
+
+ private final Token HUNK_TOKEN = new Token(HUNK_CONTENT_TYPE);
+
+ private final Token ADDED_TOKEN = new Token(ADDED_CONTENT_TYPE);
+
+ private final Token DELETED_TOKEN = new Token(REMOVED_CONTENT_TYPE);
+
+ private final Token OTHER_TOKEN = new Token(
+ IDocument.DEFAULT_CONTENT_TYPE);
+
+ private int currentOffset;
+
+ private int end;
+
+ private int tokenStart;
+
+ private int currIdx;
+
+ @Override
+ public void setRange(IDocument document, int offset, int length) {
+ Assert.isLegal(document == DiffDocument.this);
+ currentOffset = offset;
+ end = offset + length;
+ tokenStart = -1;
+ }
+
+ @Override
+ public IToken nextToken() {
+ if (tokenStart < 0) {
+ DiffStyleRange key = new DiffStyleRange();
+ key.start = currentOffset;
+ key.length = 0;
+ currIdx = Arrays.binarySearch(DiffDocument.this.ranges, key,
+ (a, b) -> {
+ if (a.start > b.start + b.length) {
+ return 1;
+ }
+ if (b.start > a.start + a.length) {
+ return -1;
+ }
+ return 0;
+ });
+ if (currIdx < 0) {
+ currIdx = -(currIdx + 1);
+ }
+ }
+ tokenStart = currentOffset;
+ if (currentOffset < end) {
+ if (currIdx >= DiffDocument.this.ranges.length) {
+ currentOffset = end;
+ return OTHER_TOKEN;
+ }
+ if (currentOffset < DiffDocument.this.ranges[currIdx].start) {
+ currentOffset = DiffDocument.this.ranges[currIdx].start;
+ return OTHER_TOKEN;
+ }
+ // We're in range[currIdx]. Typically at the beginning, but if
+ // called via setPartialRange, we may also be somewhere in the
+ // middle.
+ currentOffset += DiffDocument.this.ranges[currIdx].length
+ - (currentOffset
+ - DiffDocument.this.ranges[currIdx].start);
+ switch (DiffDocument.this.ranges[currIdx++].diffType) {
+ case HEADLINE:
+ return HEADLINE_TOKEN;
+ case HUNK:
+ return HUNK_TOKEN;
+ case ADD:
+ return ADDED_TOKEN;
+ case REMOVE:
+ return DELETED_TOKEN;
+ default:
+ return OTHER_TOKEN;
+ }
+ }
+ 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);
+ }
+
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffEditorPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffEditorPage.java
index ff00c25e40..22c23f47bf 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffEditorPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffEditorPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2015 GitHub Inc. and others.
+ * Copyright (c) 2011, 2016 GitHub Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -30,8 +30,6 @@ import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
-import org.eclipse.jface.text.Document;
-import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.source.CompositeRuler;
@@ -130,9 +128,8 @@ public class DiffEditorPage extends FormPage {
}
private void formatDiff() {
- final IDocument document = new Document();
+ final DiffDocument document = new DiffDocument();
formatter = new DiffStyleRangeFormatter(document);
- viewer.setFormatter(formatter);
Job job = new Job(UIText.DiffEditorPage_TaskGeneratingDiff) {
@@ -163,8 +160,8 @@ public class DiffEditorPage extends FormPage {
@Override
public IStatus runInUIThread(IProgressMonitor uiMonitor) {
if (UIUtils.isUsable(viewer)) {
+ document.connect(formatter);
viewer.setDocument(document);
- viewer.refreshStyleRanges();
}
return Status.OK_STATUS;
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java
index 156f242606..5ac5de71fc 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffStyleRangeFormatter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2013 GitHub Inc. and others.
+ * Copyright (c) 2011, 2016 GitHub Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -70,6 +70,11 @@ public class DiffStyleRangeFormatter extends DiffFormatter {
HEADLINE,
/**
+ * Header (after HEADLINE)
+ */
+ HEADER,
+
+ /**
* Other line
*/
OTHER,
@@ -103,6 +108,77 @@ public class DiffStyleRangeFormatter extends DiffFormatter {
}
}
+ /**
+ * Range giving access to the {@link FileDiff} and its {@link Repository}
+ * that generated the content.
+ */
+ public static class FileDiffRange {
+ private final int startOffset;
+
+ private final int endOffset;
+
+ private final FileDiff diff;
+
+ private final Repository repository;
+
+ /**
+ * Creates a new {@link FileDiffRange}
+ *
+ * @param repository
+ * the {@link FileDiff} belongs to
+ * @param fileDiff
+ * the range belongs to
+ * @param start
+ * of the range
+ * @param end
+ * of the range
+ */
+ public FileDiffRange(Repository repository, FileDiff fileDiff,
+ int start, int end) {
+ this.startOffset = start;
+ this.endOffset = end;
+ this.diff = fileDiff;
+ this.repository = repository;
+ }
+
+ /**
+ * Retrieves the start offset.
+ *
+ * @return the offset
+ */
+ public int getStartOffset() {
+ return startOffset;
+ }
+
+ /**
+ * Retrieves the end offset.
+ *
+ * @return the offset
+ */
+ public int getEndOffset() {
+ return endOffset;
+ }
+
+ /**
+ * Retrieves the {@link FileDiff}.
+ *
+ * @return the {@link FileDiff}
+ */
+ public FileDiff getDiff() {
+ return diff;
+ }
+
+ /**
+ * Retrieves the {@link Repository}.
+ *
+ * @return the {@link Repository}
+ */
+ public Repository getRepository() {
+ return repository;
+ }
+
+ }
+
private static class DocumentOutputStream extends OutputStream {
private String charset;
@@ -162,6 +238,8 @@ public class DiffStyleRangeFormatter extends DiffFormatter {
private List<DiffStyleRange> ranges = new ArrayList<>();
+ private List<FileDiffRange> fileRanges = new ArrayList<>();
+
private final int maxLines;
private int linesWritten;
@@ -205,13 +283,16 @@ public class DiffStyleRangeFormatter extends DiffFormatter {
throws IOException {
this.stream.charset = CompareCoreUtils.getResourceEncoding(repository,
diff.getPath());
+ int start = stream.offset;
diff.outputDiff(null, repository, this, true);
flush();
+ fileRanges
+ .add(new FileDiffRange(repository, diff, start, stream.offset));
return this;
}
/**
- * Get diff style ranges
+ * Get diff style ranges, sorted by offset
*
* @return non-null but possibly empty array
*/
@@ -220,6 +301,16 @@ public class DiffStyleRangeFormatter extends DiffFormatter {
}
/**
+ * Gets the file diff ranges, sorted by offset.
+ *
+ * @return the ranges
+ */
+ public FileDiffRange[] getFileRanges() {
+ return this.fileRanges
+ .toArray(new FileDiffRange[this.fileRanges.size()]);
+ }
+
+ /**
* Create and add diff style range
*
* @param type
@@ -247,6 +338,13 @@ public class DiffStyleRangeFormatter extends DiffFormatter {
protected void writeHunkHeader(int aStartLine, int aEndLine,
int bStartLine, int bEndLine) throws IOException {
int start = stream.offset;
+ if (!ranges.isEmpty()) {
+ DiffStyleRange last = ranges.get(ranges.size() - 1);
+ int lastEnd = last.start + last.length;
+ if (last.diffType == Type.HEADLINE && lastEnd < start) {
+ addRange(Type.HEADER, lastEnd, start);
+ }
+ }
super.writeHunkHeader(aStartLine, aEndLine, bStartLine, bEndLine);
stream.flushLine();
addRange(Type.HUNK, start, stream.offset);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffViewer.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffViewer.java
index e63b6ebbca..4ba83b95f8 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffViewer.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/commit/DiffViewer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2013 GitHub Inc. and others.
+ * Copyright (c) 2011, 2016 GitHub Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -8,20 +8,47 @@
* Contributors:
* Kevin Sawicki (GitHub Inc.) - initial API and implementation
* Tobias Pfeifer (SAP AG) - customizable font and color for the first header line - https://bugs.eclipse.org/397723
+ * Thomas Wolf <thomas.wolf@paranor.ch> - add hyperlinks, and use JFace syntax coloring
*******************************************************************************/
package org.eclipse.egit.ui.internal.commit;
import static org.eclipse.egit.ui.UIPreferences.THEME_DiffAddBackgroundColor;
import static org.eclipse.egit.ui.UIPreferences.THEME_DiffAddForegroundColor;
+import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHeadlineBackgroundColor;
+import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHeadlineFont;
+import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHeadlineForegroundColor;
import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHunkBackgroundColor;
import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHunkForegroundColor;
import static org.eclipse.egit.ui.UIPreferences.THEME_DiffRemoveBackgroundColor;
import static org.eclipse.egit.ui.UIPreferences.THEME_DiffRemoveForegroundColor;
-import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHeadlineBackgroundColor;
-import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHeadlineForegroundColor;
-import static org.eclipse.egit.ui.UIPreferences.THEME_DiffHeadlineFont;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.internal.util.ResourceUtil;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.internal.CompareUtils;
+import org.eclipse.egit.ui.internal.EgitUiEditorUtils;
+import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter.DiffStyleRange;
+import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter.FileDiffRange;
+import org.eclipse.egit.ui.internal.dialogs.HyperlinkSourceViewer;
+import org.eclipse.egit.ui.internal.history.FileDiff;
+import org.eclipse.egit.ui.internal.revision.GitCompareFileRevisionEditorInput;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ColorDescriptor;
@@ -29,22 +56,47 @@ import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.DeviceResourceManager;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.JFaceResources;
+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.ITextViewer;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2;
+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.ITokenScanner;
+import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.source.CompositeRuler;
+import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.LineNumberRulerColumn;
-import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
-import org.eclipse.swt.custom.LineBackgroundEvent;
-import org.eclipse.swt.custom.LineBackgroundListener;
-import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
@@ -54,34 +106,19 @@ import org.eclipse.ui.themes.IThemeManager;
/**
* Source viewer to display one or more file differences using standard editor
- * colors and fonts preferences.
+ * colors and fonts preferences. Should be used together with a
+ * {@link DiffDocument} to get proper coloring and hyperlink support.
*/
-public class DiffViewer extends SourceViewer {
-
- private DiffStyleRangeFormatter formatter;
-
- private DeviceResourceManager colors = new DeviceResourceManager(PlatformUI
- .getWorkbench().getDisplay());
-
- private LineNumberRulerColumn lineNumberRuler;
-
- private Color hunkBackgroundColor;
-
- private Color hunkForegroundColor;
+public class DiffViewer extends HyperlinkSourceViewer {
- private Color addBackgroundColor;
+ private final DeviceResourceManager colors = new DeviceResourceManager(
+ PlatformUI.getWorkbench().getDisplay());
- private Color addForegroundColor;
+ private final Map<String, IToken> tokens = new HashMap<>();
- private Color removeBackgroundColor;
+ private final Map<String, Color> backgroundColors = new HashMap<>();
- private Color removeForegroundColor;
-
- private Color headlineBackgroundColor;
-
- private Color headlineForegroundColor;
-
- private Font headlineFont;
+ private LineNumberRulerColumn lineNumberRuler;
private IPropertyChangeListener themeListener = new IPropertyChangeListener() {
@@ -98,9 +135,8 @@ public class DiffViewer extends SourceViewer {
|| THEME_DiffHeadlineFont.equals(property)
|| THEME_DiffRemoveBackgroundColor.equals(property)
|| THEME_DiffRemoveForegroundColor.equals(property)) {
- refreshDiffColors();
- refreshDiffFonts();
- refreshStyleRanges();
+ refreshDiffStyles();
+ refresh();
}
}
};
@@ -114,10 +150,21 @@ public class DiffViewer extends SourceViewer {
};
/**
+ * Creates a new {@link DiffViewer} and
+ * {@link #configure(org.eclipse.jface.text.source.SourceViewerConfiguration)
+ * configures} it with a {@link PresentationReconciler} and syntax coloring,
+ * and an {@link IHyperlinkDetector} to provide hyperlinks to open the files
+ * being diff'ed if the document used with the viewer is a
+ * {@link DiffDocument}.
+ *
* @param parent
+ * to contain the viewer
* @param ruler
+ * for the viewer (left side)
* @param styles
+ * for the viewer
* @param showCursorLine
+ * if {@code true},the current line is highlighted
*/
public DiffViewer(Composite parent, IVerticalRuler ruler, int styles,
boolean showCursorLine) {
@@ -148,28 +195,77 @@ public class DiffViewer extends SourceViewer {
colors.dispose();
}
});
- refreshDiffColors();
- refreshDiffFonts();
+ refreshDiffStyles();
styleViewer();
- }
+ configure(new HyperlinkSourceViewer.Configuration(
+ EditorsUI.getPreferenceStore()) {
- private void refreshDiffFonts() {
- FontRegistry reg = PlatformUI.getWorkbench().getThemeManager()
- .getCurrentTheme().getFontRegistry();
- this.headlineFont = reg.get(THEME_DiffHeadlineFont);
+ @Override
+ public int getHyperlinkStateMask(ISourceViewer sourceViewer) {
+ return SWT.NONE;
+ }
+
+ @Override
+ protected IHyperlinkDetector[] internalGetHyperlinkDetectors(
+ ISourceViewer sourceViewer) {
+ IHyperlinkDetector[] result = { new HyperlinkDetector() };
+ return result;
+ }
+
+ @Override
+ public String[] getConfiguredContentTypes(
+ ISourceViewer sourceViewer) {
+ return tokens.keySet().toArray(new String[tokens.size()]);
+ }
+
+ @Override
+ public IPresentationReconciler getPresentationReconciler(
+ ISourceViewer viewer) {
+ PresentationReconciler reconciler = new PresentationReconciler();
+ reconciler.setDocumentPartitioning(
+ getConfiguredDocumentPartitioning(viewer));
+ for (String contentType : tokens.keySet()) {
+ DefaultDamagerRepairer damagerRepairer = new DefaultDamagerRepairer(
+ new SingleTokenScanner(contentType));
+ reconciler.setDamager(damagerRepairer, contentType);
+ reconciler.setRepairer(damagerRepairer, contentType);
+ }
+ return reconciler;
+ }
+ });
}
- private void refreshDiffColors() {
- ColorRegistry reg = PlatformUI.getWorkbench().getThemeManager()
+ private void refreshDiffStyles() {
+ ColorRegistry col = PlatformUI.getWorkbench().getThemeManager()
.getCurrentTheme().getColorRegistry();
- this.addBackgroundColor = reg.get(THEME_DiffAddBackgroundColor);
- this.addForegroundColor = reg.get(THEME_DiffAddForegroundColor);
- this.removeBackgroundColor = reg.get(THEME_DiffRemoveBackgroundColor);
- this.removeForegroundColor = reg.get(THEME_DiffRemoveForegroundColor);
- this.hunkBackgroundColor = reg.get(THEME_DiffHunkBackgroundColor);
- this.hunkForegroundColor = reg.get(THEME_DiffHunkForegroundColor);
- this.headlineBackgroundColor = reg.get(THEME_DiffHeadlineBackgroundColor);
- this.headlineForegroundColor = reg.get(THEME_DiffHeadlineForegroundColor);
+ FontRegistry reg = PlatformUI.getWorkbench().getThemeManager()
+ .getCurrentTheme().getFontRegistry();
+ // We do the foreground via syntax coloring and the background via a
+ // line background listener. If we did the background also via the
+ // TextAttributes, this would take precedence over the line background
+ // resulting in strange display if the current line is highlighted:
+ // that highlighting would appear only beyond the end of the actual
+ // text content (i.e., beyond the end-of-line), while actual text
+ // would still get the background from the attribute.
+ tokens.put(IDocument.DEFAULT_CONTENT_TYPE, new Token(null));
+ tokens.put(DiffDocument.HEADLINE_CONTENT_TYPE,
+ new Token(new TextAttribute(
+ col.get(THEME_DiffHeadlineForegroundColor), null,
+ SWT.NORMAL, reg.get(THEME_DiffHeadlineFont))));
+ tokens.put(DiffDocument.HUNK_CONTENT_TYPE, new Token(
+ new TextAttribute(col.get(THEME_DiffHunkForegroundColor))));
+ tokens.put(DiffDocument.ADDED_CONTENT_TYPE, new Token(
+ new TextAttribute(col.get(THEME_DiffAddForegroundColor))));
+ tokens.put(DiffDocument.REMOVED_CONTENT_TYPE, new Token(
+ new TextAttribute(col.get(THEME_DiffRemoveForegroundColor))));
+ backgroundColors.put(DiffDocument.HEADLINE_CONTENT_TYPE,
+ col.get(THEME_DiffHeadlineBackgroundColor));
+ backgroundColors.put(DiffDocument.HUNK_CONTENT_TYPE,
+ col.get(THEME_DiffHunkBackgroundColor));
+ backgroundColors.put(DiffDocument.ADDED_CONTENT_TYPE,
+ col.get(THEME_DiffAddBackgroundColor));
+ backgroundColors.put(DiffDocument.REMOVED_CONTENT_TYPE,
+ col.get(THEME_DiffRemoveBackgroundColor));
}
private void initListeners() {
@@ -177,16 +273,20 @@ public class DiffViewer extends SourceViewer {
.addPropertyChangeListener(this.themeListener);
EditorsUI.getPreferenceStore().addPropertyChangeListener(
this.editorPrefListener);
- getTextWidget().addLineBackgroundListener(new LineBackgroundListener() {
-
- @Override
- public void lineGetBackground(LineBackgroundEvent event) {
- StyledText text = getTextWidget();
- if (event.lineOffset < text.getCharCount()) {
- StyleRange style = text
- .getStyleRangeAtOffset(event.lineOffset);
- if (style instanceof DiffStyleRange)
- event.lineBackground = ((DiffStyleRange) style).lineBackground;
+ getTextWidget().addLineBackgroundListener((event) -> {
+ IDocument document = getDocument();
+ if (document instanceof DiffDocument) {
+ try {
+ ITypedRegion partition = ((DiffDocument) document)
+ .getPartition(event.lineOffset);
+ if (partition != null) {
+ Color color = backgroundColors.get(partition.getType());
+ if (color != null) {
+ event.lineBackground = color;
+ }
+ }
+ } catch (BadLocationException e) {
+ // Ignore
}
}
});
@@ -236,39 +336,547 @@ public class DiffViewer extends SourceViewer {
}
}
- /** Refresh style ranges */
- public void refreshStyleRanges() {
- DiffStyleRange[] ranges = formatter != null ? formatter.getRanges()
- : new DiffStyleRange[0];
- for (DiffStyleRange range : ranges)
- switch (range.diffType) {
- case ADD:
- range.foreground = addForegroundColor;
- range.lineBackground = addBackgroundColor;
- break;
- case REMOVE:
- range.foreground = removeForegroundColor;
- range.lineBackground = removeBackgroundColor;
- break;
- case HUNK:
- range.foreground = hunkForegroundColor;
- range.lineBackground = hunkBackgroundColor;
- break;
- case HEADLINE:
- range.font = headlineFont;
- range.foreground = headlineForegroundColor;
- range.lineBackground = headlineBackgroundColor;
+ private class SingleTokenScanner implements ITokenScanner {
+
+ private final String contentType;
+
+ private int currentOffset;
+
+ private int end;
+
+ private int tokenStart;
+
+ public SingleTokenScanner(String contentType) {
+ this.contentType = contentType;
+ }
+
+ @Override
+ public void setRange(IDocument document, int offset, int length) {
+ currentOffset = offset;
+ end = offset + length;
+ tokenStart = -1;
+ }
+
+ @Override
+ public IToken nextToken() {
+ tokenStart = currentOffset;
+ if (currentOffset < end) {
+ currentOffset = end;
+ return tokens.get(contentType);
+ }
+ return Token.EOF;
+ }
+
+ @Override
+ public int getTokenOffset() {
+ return tokenStart;
+ }
+
+ @Override
+ public int getTokenLength() {
+ return currentOffset - tokenStart;
+ }
+
+ }
+
+ private class HyperlinkDetector extends AbstractHyperlinkDetector
+ implements IHyperlinkDetectorExtension2 {
+
+ private final Pattern HUNK_LINE_PATTERN = Pattern
+ .compile("@@ ([-+]?(\\d+),\\d+) ([-+]?(\\d+),\\d+) @@"); //$NON-NLS-1$
+
+ @Override
+ public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
+ IRegion region, boolean canShowMultipleHyperlinks) {
+ IDocument document = textViewer.getDocument();
+ if (textViewer != DiffViewer.this
+ || !(document instanceof DiffDocument)
+ || document.getLength() == 0) {
+ return null;
+ }
+ DiffStyleRange[] ranges = ((DiffDocument) document).getRanges();
+ FileDiffRange[] fileRanges = ((DiffDocument) document)
+ .getFileRanges();
+ if (ranges == null || ranges.length == 0 || fileRanges == null
+ || fileRanges.length == 0) {
+ return null;
+ }
+ int start = region.getOffset();
+ int end = region.getOffset() + region.getLength();
+ DiffStyleRange key = new DiffStyleRange();
+ key.start = start;
+ key.length = region.getLength();
+ int i = Arrays.binarySearch(ranges, key, (a, b) -> {
+ if (a.start > b.start + b.length) {
+ return 1;
+ }
+ if (a.start + a.length < b.start) {
+ return -1;
+ }
+ return 0;
+ });
+ List<IHyperlink> links = new ArrayList<>();
+ FileDiffRange fileRange = null;
+ for (; i >= 0 && i < ranges.length; i++) {
+ DiffStyleRange range = ranges[i];
+ if (range.start >= end) {
+ break;
+ }
+ if (range.start + range.length <= start) {
+ continue;
+ }
+ // Range overlaps region
+ switch (range.diffType) {
+ case HEADLINE:
+ fileRange = findFileRange(fileRanges, fileRange,
+ range.start);
+ if (fileRange != null) {
+ DiffEntry.ChangeType change = fileRange.getDiff()
+ .getChange();
+ switch (change) {
+ case ADD:
+ case DELETE:
+ break;
+ default:
+ if (getString(document, range.start, range.length)
+ .startsWith("diff")) { //$NON-NLS-1$
+ // "diff" is at the beginning
+ IRegion linkRegion = new Region(range.start, 4);
+ if (TextUtilities.overlaps(region,
+ linkRegion)) {
+ links.add(new CompareLink(linkRegion,
+ fileRange, -1));
+ }
+ }
+ break;
+ }
+ }
+ break;
+ case HEADER:
+ fileRange = findFileRange(fileRanges, fileRange,
+ range.start);
+ if (fileRange != null) {
+ String line = getString(document, range.start,
+ range.length);
+ createHeaderLinks((DiffDocument) document, region,
+ fileRange, range, line, DiffEntry.Side.OLD,
+ links);
+ createHeaderLinks((DiffDocument) document, region,
+ fileRange, range, line, DiffEntry.Side.NEW,
+ links);
+ }
+ break;
+ case HUNK:
+ fileRange = findFileRange(fileRanges, fileRange,
+ range.start);
+ if (fileRange != null) {
+ String line = getString(document, range.start,
+ range.length);
+ Matcher m = HUNK_LINE_PATTERN.matcher(line);
+ if (m.find()) {
+ int lineOffset = getContextLines(document, range,
+ i + 1 < ranges.length ? ranges[i + 1]
+ : null);
+ createHunkLinks(region, fileRange, range, m,
+ lineOffset, links);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (links.isEmpty()) {
+ return null;
+ }
+ return links.toArray(new IHyperlink[links.size()]);
+ }
+
+ private String getString(IDocument document, int offset, int length) {
+ try {
+ return document.get(offset, length);
+ } catch (BadLocationException e) {
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ private int getContextLines(IDocument document, DiffStyleRange hunk,
+ DiffStyleRange next) {
+ if (next != null) {
+ switch (next.diffType) {
+ case ADD:
+ case REMOVE:
+ try {
+ int diffLine = document.getLineOfOffset(next.start);
+ int hunkLine = document.getLineOfOffset(hunk.start);
+ return diffLine - hunkLine - 1;
+ } catch (BadLocationException e) {
+ // Ignore
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+ }
+
+ private FileDiffRange findFileRange(FileDiffRange[] ranges,
+ FileDiffRange candidate, int offset) {
+ if (candidate != null && candidate.getStartOffset() <= offset
+ && candidate.getEndOffset() > offset) {
+ return candidate;
+ }
+ FileDiffRange key = new FileDiffRange(null, null, offset, offset);
+ int i = Arrays.binarySearch(ranges, key, (a, b) -> {
+ if (a.getStartOffset() > b.getEndOffset()) {
+ return 1;
+ }
+ if (b.getStartOffset() > a.getEndOffset()) {
+ return -1;
+ }
+ return 0;
+ });
+ return i >= 0 ? ranges[i] : null;
+ }
+
+ private void createHeaderLinks(DiffDocument document, IRegion region,
+ FileDiffRange fileRange, DiffStyleRange range, String line,
+ DiffEntry.Side side, List<IHyperlink> links) {
+ Pattern p = document.getPathPattern(side);
+ if (p == null) {
+ return;
+ }
+ DiffEntry.ChangeType change = fileRange.getDiff().getChange();
+ switch (side) {
+ case OLD:
+ if (change == DiffEntry.ChangeType.ADD) {
+ return;
+ }
break;
default:
+ if (change == DiffEntry.ChangeType.DELETE) {
+ return;
+ }
break;
+
}
- getTextWidget().setStyleRanges(ranges);
+ Matcher m = p.matcher(line);
+ if (m.find()) {
+ IRegion linkRegion = new Region(range.start + m.start(),
+ m.end() - m.start());
+ if (TextUtilities.overlaps(region, linkRegion)) {
+ if (side == DiffEntry.Side.NEW) {
+ File file = new Path(fileRange.getRepository()
+ .getWorkTree().getAbsolutePath()).append(
+ fileRange.getDiff().getNewPath())
+ .toFile();
+ if (file.exists()) {
+ links.add(new FileLink(linkRegion, file, -1));
+ }
+ }
+ links.add(new OpenLink(linkRegion, fileRange, side, -1));
+ }
+ }
+ }
+
+ private void createHunkLinks(IRegion region, FileDiffRange fileRange,
+ DiffStyleRange range, Matcher m, int lineOffset,
+ List<IHyperlink> links) {
+ DiffEntry.ChangeType change = fileRange.getDiff().getChange();
+ if (change != DiffEntry.ChangeType.ADD) {
+ IRegion linkRegion = new Region(range.start + m.start(1),
+ m.end(1) - m.start(1));
+ if (TextUtilities.overlaps(linkRegion, region)) {
+ int lineNo = Integer.parseInt(m.group(2)) - 1 + lineOffset;
+ if (change != DiffEntry.ChangeType.DELETE) {
+ links.add(
+ new CompareLink(linkRegion, fileRange, lineNo));
+ }
+ links.add(new OpenLink(linkRegion, fileRange,
+ DiffEntry.Side.OLD, lineNo));
+ }
+ }
+ if (change != DiffEntry.ChangeType.DELETE) {
+ IRegion linkRegion = new Region(range.start + m.start(3),
+ m.end(3) - m.start(3));
+ if (TextUtilities.overlaps(linkRegion, region)) {
+ int lineNo = Integer.parseInt(m.group(4)) - 1 + lineOffset;
+ if (change != DiffEntry.ChangeType.ADD) {
+ links.add(
+ new CompareLink(linkRegion, fileRange, lineNo));
+ }
+ File file = new Path(fileRange.getRepository().getWorkTree()
+ .getAbsolutePath())
+ .append(fileRange.getDiff().getNewPath())
+ .toFile();
+ if (file.exists()) {
+ links.add(new FileLink(linkRegion, file, lineNo));
+ }
+ links.add(new OpenLink(linkRegion, fileRange,
+ DiffEntry.Side.NEW, lineNo));
+ }
+ }
+ }
+
+ @Override
+ public int getStateMask() {
+ return -1;
+ }
+ }
+
+ private static abstract class RevealLink implements IHyperlink {
+
+ private final IRegion region;
+
+ protected final int lineNo;
+
+ protected RevealLink(IRegion region, int lineNo) {
+ this.region = region;
+ this.lineNo = lineNo;
+ }
+
+ @Override
+ public IRegion getHyperlinkRegion() {
+ return region;
+ }
+
+ @Override
+ public String getTypeLabel() {
+ return null;
+ }
+
+ }
+
+ private static class FileLink extends RevealLink {
+
+ private final File file;
+
+ public FileLink(IRegion region, File file, int lineNo) {
+ super(region, lineNo);
+ this.file = file;
+ }
+
+ @Override
+ public String getHyperlinkText() {
+ return UIText.DiffViewer_OpenWorkingTreeLinkLabel;
+ }
+
+ @Override
+ public void open() {
+ openFileInEditor(file, lineNo);
+ }
+
+ }
+
+ private static class CompareLink extends RevealLink {
+
+ protected final Repository repository;
+
+ protected final FileDiff fileDiff;
+
+ public CompareLink(IRegion region, FileDiffRange fileRange,
+ int lineNo) {
+ super(region, lineNo);
+ this.repository = fileRange.getRepository();
+ this.fileDiff = fileRange.getDiff();
+ }
+
+ @Override
+ public String getHyperlinkText() {
+ return UIText.DiffViewer_OpenComparisonLinkLabel;
+ }
+
+ @Override
+ public void open() {
+ // No way to selectAndReveal a line or a diff node in a
+ // CompareEditor?
+ showTwoWayFileDiff(repository, fileDiff);
+ }
+
}
- /** @param formatter */
- public void setFormatter(DiffStyleRangeFormatter formatter) {
- this.formatter = formatter;
- refreshStyleRanges();
+ private static class OpenLink extends CompareLink {
+
+ private final DiffEntry.Side side;
+
+ public OpenLink(IRegion region, FileDiffRange fileRange,
+ DiffEntry.Side side, int lineNo) {
+ super(region, fileRange, lineNo);
+ this.side = side;
+ }
+
+ @Override
+ public String getHyperlinkText() {
+ switch (side) {
+ case OLD:
+ return UIText.DiffViewer_OpenPreviousLinkLabel;
+ default:
+ return UIText.DiffViewer_OpenInEditorLinkLabel;
+ }
+ }
+
+ @Override
+ public void open() {
+ openInEditor(repository, fileDiff, side, lineNo);
+ }
+
+ }
+
+ /**
+ * Opens the file, if it exists, in an editor.
+ *
+ * @param file
+ * to open
+ * @param lineNoToReveal
+ * if >= 0, select and reveals the given line
+ */
+ public static void openFileInEditor(File file, int lineNoToReveal) {
+ if (!file.exists()) {
+ Activator.showError(
+ NLS.bind(UIText.DiffViewer_FileDoesNotExist,
+ file.getPath()),
+ null);
+ return;
+ }
+ IWorkbenchPage page = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getActivePage();
+ IEditorPart editor = EgitUiEditorUtils.openEditor(file, page);
+ EgitUiEditorUtils.revealLine(editor, lineNoToReveal);
+ }
+
+ /**
+ * Opens either the new or the old version of a {@link FileDiff} in an
+ * editor.
+ *
+ * @param repository
+ * the {@link FileDiff} belongs to
+ * @param d
+ * the {@link FileDiff}
+ * @param side
+ * to show
+ * @param lineNoToReveal
+ * if >= 0, select and reveals the given line
+ */
+ public static void openInEditor(Repository repository, FileDiff d,
+ DiffEntry.Side side, int lineNoToReveal) {
+ ObjectId[] blobs = d.getBlobs();
+ switch (side) {
+ case OLD:
+ openInEditor(repository, d.getOldPath(), d.getCommit().getParent(0),
+ blobs[0], lineNoToReveal);
+ break;
+ default:
+ openInEditor(repository, d.getNewPath(), d.getCommit(),
+ blobs[blobs.length - 1], lineNoToReveal);
+ break;
+ }
+ }
+
+ private static void openInEditor(Repository repository, String path,
+ RevCommit commit, ObjectId blob, int reveal) {
+ try {
+ IFileRevision rev = CompareUtils.getFileRevision(path, commit,
+ repository, blob);
+ if (rev == null) {
+ String message = NLS.bind(
+ UIText.DiffViewer_notContainedInCommit, path,
+ commit.getName());
+ Activator.showError(message, null);
+ return;
+ }
+ IWorkbenchWindow window = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ IEditorPart editor = EgitUiEditorUtils.openEditor(page, rev,
+ new NullProgressMonitor());
+ EgitUiEditorUtils.revealLine(editor, reveal);
+ } catch (IOException | CoreException e) {
+ Activator.handleError(UIText.GitHistoryPage_openFailed, e, true);
+ }
+ }
+
+ /**
+ * Shows a two-way diff between the old and new versions of a
+ * {@link FileDiff} in a compare editor.
+ *
+ * @param repository
+ * the {@link FileDiff} belongs to
+ * @param d
+ * the {@link FileDiff} to show
+ */
+ public static void showTwoWayFileDiff(Repository repository, FileDiff d) {
+ String np = d.getNewPath();
+ String op = d.getOldPath();
+ RevCommit c = d.getCommit();
+ ObjectId[] blobs = d.getBlobs();
+
+ // extract commits
+ final RevCommit oldCommit;
+ final ObjectId oldObjectId;
+ if (!d.getChange().equals(ChangeType.ADD)) {
+ oldCommit = c.getParent(0);
+ oldObjectId = blobs[0];
+ } else {
+ // Initial import
+ oldCommit = null;
+ oldObjectId = null;
+ }
+
+ final RevCommit newCommit;
+ final ObjectId newObjectId;
+ if (d.getChange().equals(ChangeType.DELETE)) {
+ newCommit = null;
+ newObjectId = null;
+ } else {
+ newCommit = c;
+ newObjectId = blobs[blobs.length - 1];
+ }
+ IWorkbenchWindow window = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ if (oldCommit != null && newCommit != null && repository != null) {
+ IFile file = np != null
+ ? ResourceUtil.getFileForLocation(repository, np, false)
+ : null;
+ try {
+ if (file != null) {
+ IResource[] resources = new IResource[] { file, };
+ CompareUtils.compare(resources, repository, np, op,
+ newCommit.getName(), oldCommit.getName(), false,
+ page);
+ } else {
+ IPath location = new Path(
+ repository.getWorkTree().getAbsolutePath())
+ .append(np);
+ CompareUtils.compare(location, repository,
+ newCommit.getName(), oldCommit.getName(), false,
+ page);
+ }
+ } catch (IOException e) {
+ Activator.handleError(UIText.GitHistoryPage_openFailed, e,
+ true);
+ }
+ return;
+ }
+
+ // still happens on initial commits
+ final ITypedElement oldSide = createTypedElement(repository, op,
+ oldCommit, oldObjectId);
+ final ITypedElement newSide = createTypedElement(repository, np,
+ newCommit, newObjectId);
+ CompareUtils.openInCompare(page,
+ new GitCompareFileRevisionEditorInput(newSide, oldSide, null));
+ }
+
+ private static ITypedElement createTypedElement(Repository repository,
+ String path, final RevCommit commit, final ObjectId objectId) {
+ if (null != commit) {
+ return CompareUtils.getFileRevisionTypedElement(path, commit,
+ repository, objectId);
+ } else {
+ return new GitCompareFileRevisionEditorInput.EmptyTypedElement(""); //$NON-NLS-1$
+ }
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java
index 73902a521b..19f52d2d2a 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CompareTreeView.java
@@ -41,10 +41,10 @@ import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.CompareUtils;
-import org.eclipse.egit.ui.internal.EgitUiEditorUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.actions.BooleanPrefAction;
+import org.eclipse.egit.ui.internal.commit.DiffViewer;
import org.eclipse.egit.ui.internal.dialogs.CompareTreeView.PathNode.Type;
import org.eclipse.egit.ui.internal.revision.FileRevisionTypedElement;
import org.eclipse.egit.ui.internal.revision.GitCompareFileRevisionEditorInput;
@@ -99,8 +99,6 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.ui.ISharedImages;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
import org.eclipse.ui.model.WorkbenchLabelProvider;
@@ -278,8 +276,10 @@ public class CompareTreeView extends ViewPart implements IMenuListener, IShowInS
compareInput);
} else {
IFile file = fileNode.getFile();
- if (file != null)
- openFileInEditor(file.getLocation().toOSString());
+ if (file != null) {
+ DiffViewer.openFileInEditor(file.getLocation().toFile(),
+ -1);
+ }
}
}
}
@@ -1114,18 +1114,6 @@ public class CompareTreeView extends ViewPart implements IMenuListener, IShowInS
getSite().registerContextMenu(manager, tree);
}
- private void openFileInEditor(String filePath) {
- IWorkbenchWindow window = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow();
- File file = new File(filePath);
- if (!file.exists()) {
- String message = NLS.bind(UIText.CommitFileDiffViewer_FileDoesNotExist, filePath);
- Activator.showError(message, null);
- }
- IWorkbenchPage page = window.getActivePage();
- EgitUiEditorUtils.openEditor(file, page);
- }
-
private IAction createOpenAction(ITreeSelection selection) {
final List<String> pathsToOpen = getSelectedPaths(selection);
if (pathsToOpen == null || pathsToOpen.isEmpty())
@@ -1133,10 +1121,12 @@ public class CompareTreeView extends ViewPart implements IMenuListener, IShowInS
return new Action(
UIText.CommitFileDiffViewer_OpenWorkingTreeVersionInEditorMenuLabel) {
+
@Override
public void run() {
- for (String filePath : pathsToOpen)
- openFileInEditor(filePath);
+ for (String filePath : pathsToOpen) {
+ DiffViewer.openFileInEditor(new File(filePath), -1);
+ }
}
};
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkSourceViewer.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkSourceViewer.java
index 4da7f6ff4d..346936aad6 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkSourceViewer.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/HyperlinkSourceViewer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (C) 2015 Thomas Wolf <thomas.wolf@paranor.ch>.
+ * Copyright (C) 2015, 2016 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
@@ -13,7 +13,6 @@ import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
-import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IRegion;
@@ -29,6 +28,7 @@ import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
@@ -43,7 +43,7 @@ import org.eclipse.ui.texteditor.HyperlinkDetectorDescriptor;
*/
public class HyperlinkSourceViewer extends SourceViewer {
// The default SourceViewer doesn't do this and instead AbstractTextEditor
- // has code that does all that. For our uses,it is much more convenient if
+ // has code that does all that. For our uses it is much more convenient if
// the viewer itself handles this.
private Configuration configuration;
@@ -142,11 +142,12 @@ public class HyperlinkSourceViewer extends SourceViewer {
preferenceKeysForActivation
.add(AbstractTextEditor.PREFERENCE_HYPERLINK_KEY_MODIFIER);
// All applicable individual hyperlink detectors settings.
- Map targets = configuration.getHyperlinkDetectorTargets(this);
+ Set<?> targets = configuration.getHyperlinkDetectorTargets(this)
+ .keySet();
for (HyperlinkDetectorDescriptor desc : EditorsUI
.getHyperlinkDetectorRegistry()
.getHyperlinkDetectorDescriptors()) {
- if (targets.keySet().contains(desc.getTargetId())) {
+ if (targets.contains(desc.getTargetId())) {
preferenceKeysForEnablement.add(desc.getId());
preferenceKeysForActivation.add(desc.getId()
+ HyperlinkDetectorDescriptor.STATE_MASK_POSTFIX);
@@ -206,7 +207,7 @@ public class HyperlinkSourceViewer extends SourceViewer {
* {@link #internalGetHyperlinkDetectors(ISourceViewer)} to get the
* hyperlink detectors.
* <p>
- * Sets up the hyperlink detetctors such that they are active on both
+ * Sets up the hyperlink detectors such that they are active on both
* {@link SWT#NONE} and on the configured modifier key combination if
* the viewer is configured to open hyperlinks on direct click, i.e., if
* {@link TextSourceViewerConfiguration#getHyperlinkStateMask(ISourceViewer)
@@ -275,8 +276,11 @@ public class HyperlinkSourceViewer extends SourceViewer {
return super.getHyperlinkDetectors(sourceViewer);
}
+ @SuppressWarnings("unchecked")
@Override
protected Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) {
+ // TODO: use generified signature once EGit's base dependency is
+ // Eclipse 4.5.
// Just so that we have visibility on this in the enclosing class.
return super.getHyperlinkDetectorTargets(sourceViewer);
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitFileDiffViewer.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitFileDiffViewer.java
index 2df64af7dc..a48a58bb1d 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitFileDiffViewer.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/history/CommitFileDiffViewer.java
@@ -20,12 +20,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import org.eclipse.compare.ITypedElement;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.egit.core.internal.job.JobUtil;
import org.eclipse.egit.core.internal.storage.CommitFileRevision;
@@ -36,11 +33,10 @@ import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.ActionUtils;
import org.eclipse.egit.ui.internal.CompareUtils;
-import org.eclipse.egit.ui.internal.EgitUiEditorUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.blame.BlameOperation;
-import org.eclipse.egit.ui.internal.revision.GitCompareFileRevisionEditorInput;
+import org.eclipse.egit.ui.internal.commit.DiffViewer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
@@ -59,9 +55,9 @@ import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -144,7 +140,8 @@ public class CommitFileDiffViewer extends TableViewer {
*
* @param parent
* @param site
- * @param style SWT style bits
+ * @param style
+ * SWT style bits
*/
public CommitFileDiffViewer(final Composite parent,
final IWorkbenchSite site, final int style) {
@@ -163,28 +160,21 @@ public class CommitFileDiffViewer extends TableViewer {
addOpenListener(new IOpenListener() {
@Override
public void open(final OpenEvent event) {
- final ISelection s = event.getSelection();
- if (s.isEmpty() || !(s instanceof IStructuredSelection))
+ ISelection s = event.getSelection();
+ if (s.isEmpty() || !(s instanceof IStructuredSelection)) {
return;
- final IStructuredSelection iss = (IStructuredSelection) s;
- final FileDiff d = (FileDiff) iss.getFirstElement();
+ }
+ IStructuredSelection iss = (IStructuredSelection) s;
+ FileDiff d = (FileDiff) iss.getFirstElement();
if (Activator.getDefault().getPreferenceStore().getBoolean(
UIPreferences.RESOURCEHISTORY_COMPARE_MODE)) {
- if (d.getBlobs().length <= 2)
- showTwoWayFileDiff(d);
- else
- MessageDialog
- .openInformation(
- PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow()
- .getShell(),
- UIText.CommitFileDiffViewer_CanNotOpenCompareEditorTitle,
- UIText.CommitFileDiffViewer_MergeCommitMultiAncestorMessage);
+ showTwoWayFileDiff(d);
} else {
- if (d.getChange() == ChangeType.DELETE)
+ if (d.getChange() == ChangeType.DELETE) {
openPreviousVersionInEditor(d);
- else
+ } else {
openThisVersionInEditor(d);
+ }
}
}
});
@@ -208,7 +198,8 @@ public class CommitFileDiffViewer extends TableViewer {
Control c = getControl();
c.setMenu(mgr.createContextMenu(c));
- openThisVersion = new Action(UIText.CommitFileDiffViewer_OpenInEditorMenuLabel) {
+ openThisVersion = new Action(
+ UIText.CommitFileDiffViewer_OpenInEditorMenuLabel) {
@Override
public void run() {
final ISelection s = getSelection();
@@ -233,8 +224,7 @@ public class CommitFileDiffViewer extends TableViewer {
}
};
- blame = new Action(
- UIText.CommitFileDiffViewer_ShowAnnotationsMenuLabel,
+ blame = new Action(UIText.CommitFileDiffViewer_ShowAnnotationsMenuLabel,
UIIcons.ANNOTATE) {
@Override
public void run() {
@@ -257,10 +247,10 @@ public class CommitFileDiffViewer extends TableViewer {
final IStructuredSelection iss = (IStructuredSelection) s;
for (Iterator<FileDiff> it = iss.iterator(); it.hasNext();) {
String relativePath = it.next().getPath();
- String path = new Path(getRepository().getWorkTree()
- .getAbsolutePath()).append(relativePath)
- .toOSString();
- openFileInEditor(path);
+ File file = new Path(
+ getRepository().getWorkTree().getAbsolutePath())
+ .append(relativePath).toFile();
+ DiffViewer.openFileInEditor(file, -1);
}
}
};
@@ -268,21 +258,13 @@ public class CommitFileDiffViewer extends TableViewer {
compare = new Action(UIText.CommitFileDiffViewer_CompareMenuLabel) {
@Override
public void run() {
- final ISelection s = getSelection();
- if (s.isEmpty() || !(s instanceof IStructuredSelection))
+ ISelection s = getSelection();
+ if (s.isEmpty() || !(s instanceof IStructuredSelection)) {
return;
- final IStructuredSelection iss = (IStructuredSelection) s;
- final FileDiff d = (FileDiff) iss.getFirstElement();
- if (d.getBlobs().length <= 2)
- showTwoWayFileDiff(d);
- else
- MessageDialog
- .openInformation(
- PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow()
- .getShell(),
- UIText.CommitFileDiffViewer_CanNotOpenCompareEditorTitle,
- UIText.CommitFileDiffViewer_MergeCommitMultiAncestorMessage);
+ }
+ IStructuredSelection iss = (IStructuredSelection) s;
+ FileDiff d = (FileDiff) iss.getFirstElement();
+ showTwoWayFileDiff(d);
}
};
@@ -299,7 +281,8 @@ public class CommitFileDiffViewer extends TableViewer {
};
showInHistory = new Action(
- UIText.CommitFileDiffViewer_ShowInHistoryLabel, UIIcons.HISTORY) {
+ UIText.CommitFileDiffViewer_ShowInHistoryLabel,
+ UIIcons.HISTORY) {
@Override
public void run() {
ShowInContext context = getShowInContext();
@@ -327,8 +310,8 @@ public class CommitFileDiffViewer extends TableViewer {
mgr.add(new Separator());
mgr.add(showInHistory);
- MenuManager showInSubMenu = UIUtils.createShowInMenu(site
- .getWorkbenchWindow());
+ MenuManager showInSubMenu = UIUtils
+ .createShowInMenu(site.getWorkbenchWindow());
mgr.add(showInSubMenu);
mgr.add(new Separator());
@@ -383,9 +366,9 @@ public class CommitFileDiffViewer extends TableViewer {
blame.setEnabled(oneOrMoreSelected);
if (sel.size() == 1 && !db.isBare()) {
FileDiff diff = (FileDiff) sel.getFirstElement();
- String path = new Path(getRepository().getWorkTree()
- .getAbsolutePath()).append(diff.getPath())
- .toOSString();
+ String path = new Path(
+ getRepository().getWorkTree().getAbsolutePath())
+ .append(diff.getPath()).toOSString();
boolean workTreeFileExists = new File(path).exists();
compareWorkingTreeVersion.setEnabled(workTreeFileExists);
openWorkingTreeVersion.setEnabled(workTreeFileExists);
@@ -437,57 +420,16 @@ public class CommitFileDiffViewer extends TableViewer {
historyPageInput = new HistoryPageInput(db,
files.toArray(new File[files.size()]));
}
- return new ShowInContext(historyPageInput, new StructuredSelection(
- elements));
- }
-
- private void openFileInEditor(String filePath) {
- IWorkbenchWindow window = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow();
- File file = new File(filePath);
- if (!file.exists()) {
- String message = NLS.bind(UIText.CommitFileDiffViewer_FileDoesNotExist, filePath);
- Activator.showError(message, null);
- }
- IWorkbenchPage page = window.getActivePage();
- EgitUiEditorUtils.openEditor(file, page);
+ return new ShowInContext(historyPageInput,
+ new StructuredSelection(elements));
}
private void openThisVersionInEditor(FileDiff d) {
- ObjectId[] blobs = d.getBlobs();
- ObjectId blob = blobs[blobs.length - 1];
- openInEditor(d.getNewPath(), d.getCommit(), blob);
+ DiffViewer.openInEditor(getRepository(), d, DiffEntry.Side.NEW, -1);
}
private void openPreviousVersionInEditor(FileDiff d) {
- RevCommit commit = d.getCommit().getParent(0);
- ObjectId blob = d.getBlobs()[0];
- openInEditor(d.getOldPath(), commit, blob);
- }
-
- private void openInEditor(String path, RevCommit commit, ObjectId blob) {
- try {
- IFileRevision rev = CompareUtils.getFileRevision(path, commit,
- getRepository(), blob);
- if (rev != null) {
- IWorkbenchWindow window = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow();
- IWorkbenchPage page = window.getActivePage();
- EgitUiEditorUtils.openEditor(page, rev,
- new NullProgressMonitor());
- } else {
- String message = NLS.bind(
- UIText.CommitFileDiffViewer_notContainedInCommit, path,
- commit.getName());
- Activator.showError(message, null);
- }
- } catch (IOException e) {
- Activator.logError(UIText.GitHistoryPage_openFailed, e);
- Activator.showError(UIText.GitHistoryPage_openFailed, null);
- } catch (CoreException e) {
- Activator.logError(UIText.GitHistoryPage_openFailed, e);
- Activator.showError(UIText.GitHistoryPage_openFailed, null);
- }
+ DiffViewer.openInEditor(getRepository(), d, DiffEntry.Side.OLD, -1);
}
private void showAnnotations(FileDiff d) {
@@ -495,8 +437,8 @@ public class CommitFileDiffViewer extends TableViewer {
IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
- RevCommit commit = d.getChange().equals(ChangeType.DELETE) ? d
- .getCommit().getParent(0) : d.getCommit();
+ RevCommit commit = d.getChange().equals(ChangeType.DELETE)
+ ? d.getCommit().getParent(0) : d.getCommit();
String path = d.getPath();
IFileRevision rev = CompareUtils.getFileRevision(path, commit,
getRepository(),
@@ -509,8 +451,8 @@ public class CommitFileDiffViewer extends TableViewer {
JobFamilies.BLAME);
} else {
String message = NLS.bind(
- UIText.CommitFileDiffViewer_notContainedInCommit,
- path, d.getCommit().getId().getName());
+ UIText.DiffViewer_notContainedInCommit, path,
+ d.getCommit().getId().getName());
Activator.showError(message, null);
}
} catch (IOException e) {
@@ -520,71 +462,15 @@ public class CommitFileDiffViewer extends TableViewer {
}
void showTwoWayFileDiff(final FileDiff d) {
- final String np = d.getNewPath();
- final String op = d.getOldPath();
- final RevCommit c = d.getCommit();
-
- // extract commits
- final RevCommit oldCommit;
- final ObjectId oldObjectId;
- if (d.getBlobs().length == 2 && !d.getChange().equals(ChangeType.ADD)) {
- oldCommit = c.getParent(0);
- oldObjectId = d.getBlobs()[0];
- } else {
- // Initial import
- oldCommit = null;
- oldObjectId = null;
- }
-
- final RevCommit newCommit;
- final ObjectId newObjectId;
- if (d.getChange().equals(ChangeType.DELETE)) {
- newCommit = null;
- newObjectId = null;
+ if (d.getBlobs().length <= 2) {
+ DiffViewer.showTwoWayFileDiff(getRepository(), d);
} else {
- newCommit = c;
- newObjectId = d.getBlobs()[1];
- }
-
- IWorkbenchPage page = site.getWorkbenchWindow().getActivePage();
- if (oldCommit != null && newCommit != null) {
- IFile file = ResourceUtil.getFileForLocation(getRepository(), np, false);
- try {
- if (file != null) {
- IResource[] resources = new IResource[] { file, };
- CompareUtils.compare(resources, getRepository(), np, op,
- newCommit.getName(), oldCommit.getName(), false,
- page);
- } else {
- IPath location = new Path(getRepository().getWorkTree()
- .getAbsolutePath()).append(np);
- CompareUtils.compare(location, getRepository(),
- newCommit.getName(), oldCommit.getName(), false,
- page);
- }
- } catch (Exception e) {
- Activator.logError(UIText.GitHistoryPage_openFailed, e);
- Activator.showError(UIText.GitHistoryPage_openFailed, null);
- }
- return;
+ MessageDialog.openInformation(
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(),
+ UIText.CommitFileDiffViewer_CanNotOpenCompareEditorTitle,
+ UIText.CommitFileDiffViewer_MergeCommitMultiAncestorMessage);
}
-
- // still happens on initial commits
- final ITypedElement oldSide = createTypedElement(op, oldCommit,
- oldObjectId);
- final ITypedElement newSide = createTypedElement(np, newCommit,
- newObjectId);
- CompareUtils.openInCompare(page, new GitCompareFileRevisionEditorInput(
- newSide, oldSide, null));
- }
-
- private ITypedElement createTypedElement(final String path,
- final RevCommit commit, final ObjectId objectId) {
- if (null != commit)
- return CompareUtils.getFileRevisionTypedElement(path, commit,
- getRepository(), objectId);
- else
- return new GitCompareFileRevisionEditorInput.EmptyTypedElement(""); //$NON-NLS-1$
}
void showWorkingDirectoryFileDiff(final FileDiff d) {
@@ -601,11 +487,12 @@ public class CommitFileDiffViewer extends TableViewer {
try {
if (file != null) {
final IResource[] resources = new IResource[] { file, };
- CompareUtils.compare(resources, getRepository(),
- Constants.HEAD, commit.getName(), true, activePage);
+ CompareUtils.compare(resources, getRepository(), Constants.HEAD,
+ commit.getName(), true, activePage);
} else {
- IPath path = new Path(getRepository().getWorkTree()
- .getAbsolutePath()).append(p);
+ IPath path = new Path(
+ getRepository().getWorkTree().getAbsolutePath())
+ .append(p);
File ioFile = path.toFile();
if (ioFile.exists())
CompareUtils.compare(path, getRepository(), Constants.HEAD,
@@ -679,7 +566,8 @@ public class CommitFileDiffViewer extends TableViewer {
* @param interestingPaths
*/
void setInterestingPaths(Set<String> interestingPaths) {
- ((FileDiffContentProvider) getContentProvider()).setInterestingPaths(interestingPaths);
+ ((FileDiffContentProvider) getContentProvider())
+ .setInterestingPaths(interestingPaths);
}
void selectFirstInterestingElement() {
@@ -688,8 +576,8 @@ public class CommitFileDiffViewer extends TableViewer {
for (final Object element : elements) {
if (element instanceof FileDiff) {
FileDiff fileDiff = (FileDiff) element;
- boolean marked = fileDiff
- .isMarked(FileDiffContentProvider.INTERESTING_MARK_TREE_FILTER_INDEX);
+ boolean marked = fileDiff.isMarked(
+ FileDiffContentProvider.INTERESTING_MARK_TREE_FILTER_INDEX);
if (marked) {
setSelection(new StructuredSelection(fileDiff));
return;
@@ -707,7 +595,8 @@ public class CommitFileDiffViewer extends TableViewer {
for (final Object element : elements) {
if (element instanceof FileDiff) {
FileDiff fileDiff = (FileDiff) element;
- boolean marked = fileDiff.isMarked(FileDiffContentProvider.INTERESTING_MARK_TREE_FILTER_INDEX);
+ boolean marked = fileDiff.isMarked(
+ FileDiffContentProvider.INTERESTING_MARK_TREE_FILTER_INDEX);
if (marked) {
// Does not yet work reliably, see comment on bug 393610.
getTable().getDisplay().asyncExec(new Runnable() {
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 f09ad4e87f..4cee73347e 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,7 +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, 2016 Thomas Wolf <thomas.wolf@paranor.ch>
+ * Copyright (C) 2015-2016 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
@@ -45,6 +45,7 @@ import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.CompareUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.commit.DiffDocument;
import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter;
import org.eclipse.egit.ui.internal.commit.DiffViewer;
import org.eclipse.egit.ui.internal.dialogs.HyperlinkSourceViewer;
@@ -2297,7 +2298,6 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener,
if (UIUtils.isUsable(diffViewer)) {
IDocument document = new Document();
diffViewer.setDocument(document);
- diffViewer.setFormatter(null);
}
return;
}
@@ -2311,7 +2311,7 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener,
}
int maxLines = Activator.getDefault().getPreferenceStore()
.getInt(UIPreferences.HISTORY_MAX_DIFF_LINES);
- final IDocument document = new Document();
+ final DiffDocument document = new DiffDocument();
final DiffStyleRangeFormatter formatter = new DiffStyleRangeFormatter(
document, document.getLength(), maxLines);
@@ -2345,8 +2345,8 @@ public class GitHistoryPage extends HistoryPage implements RefsChangedListener,
return Status.CANCEL_STATUS;
}
if (UIUtils.isUsable(diffViewer)) {
+ document.connect(formatter);
diffViewer.setDocument(document);
- diffViewer.setFormatter(formatter);
resizeCommentAndDiffScrolledComposite();
}
return Status.OK_STATUS;
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/GitCompareFileRevisionEditorInput.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/GitCompareFileRevisionEditorInput.java
index ae62d9c404..bcde8d75b1 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/GitCompareFileRevisionEditorInput.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/GitCompareFileRevisionEditorInput.java
@@ -45,9 +45,6 @@ import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.osgi.util.NLS;
@@ -65,7 +62,6 @@ import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.SaveablesLifecycleEvent;
-import org.eclipse.ui.texteditor.ITextEditor;
/**
* The input provider for the compare editor when working on resources
@@ -533,24 +529,7 @@ public class GitCompareFileRevisionEditorInput extends SaveableCompareEditorInpu
IWorkbenchPage page = window.getActivePage();
IEditorPart editor = EgitUiEditorUtils.openEditor(workspaceFile,
page);
- selectLine(editor, selectedLine);
- }
-
- private void selectLine(IEditorPart editorPart, int selectedLine) {
- if (editorPart instanceof ITextEditor) {
- ITextEditor editor = (ITextEditor) editorPart;
- IDocument document = editor.getDocumentProvider().getDocument(
- editor.getEditorInput());
- if (document != null)
- try {
- IRegion line = document
- .getLineInformation(selectedLine);
- editor.selectAndReveal(line.getOffset(), 0);
- } catch (BadLocationException e) {
- // line seems not to exist in
- // workspace version
- }
- }
+ EgitUiEditorUtils.revealLine(editor, selectedLine);
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java
index f1f0eac4db..ccfa1c30b1 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java
@@ -67,7 +67,6 @@ import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.CommonUtils;
-import org.eclipse.egit.ui.internal.EgitUiEditorUtils;
import org.eclipse.egit.ui.internal.GitLabels;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
@@ -84,6 +83,7 @@ import org.eclipse.egit.ui.internal.commit.CommitJob;
import org.eclipse.egit.ui.internal.commit.CommitJob.PushMode;
import org.eclipse.egit.ui.internal.commit.CommitMessageHistory;
import org.eclipse.egit.ui.internal.commit.CommitProposalProcessor;
+import org.eclipse.egit.ui.internal.commit.DiffViewer;
import org.eclipse.egit.ui.internal.components.ToggleableWarningLabel;
import org.eclipse.egit.ui.internal.decorators.IProblemDecoratable;
import org.eclipse.egit.ui.internal.decorators.ProblemLabelDecorator;
@@ -156,7 +156,6 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.VerifyKeyListener;
@@ -202,7 +201,6 @@ import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.IViewSite;
-import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
@@ -2947,26 +2945,13 @@ public class StagingView extends ViewPart implements IShowInSource {
if (element instanceof StagingEntry) {
StagingEntry entry = (StagingEntry) element;
String relativePath = entry.getPath();
- String path = new Path(repo.getWorkTree()
- .getAbsolutePath()).append(relativePath)
- .toOSString();
- openFileInEditor(path);
+ File file = new Path(repo.getWorkTree().getAbsolutePath())
+ .append(relativePath).toFile();
+ DiffViewer.openFileInEditor(file, -1);
}
}
}
- private void openFileInEditor(String filePath) {
- IWorkbenchWindow window = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow();
- File file = new File(filePath);
- if (!file.exists()) {
- String message = NLS.bind(UIText.CommitFileDiffViewer_FileDoesNotExist, filePath);
- Activator.showError(message, null);
- }
- IWorkbenchPage page = window.getActivePage();
- EgitUiEditorUtils.openEditor(file, page);
- }
-
private static Set<StagingEntry.Action> getAvailableActions(IStructuredSelection selection) {
Set<StagingEntry.Action> availableActions = EnumSet.noneOf(StagingEntry.Action.class);
for (Iterator it = selection.iterator(); it.hasNext(); ) {
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 9abe55e6b7..d972b2af53 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
@@ -1518,15 +1518,19 @@ MultiPullResultDialog_WindowTitle=Pull Result for Multiple Repositories
CommitFileDiffViewer_CanNotOpenCompareEditorTitle=Cannot Open Compare Editor
CommitFileDiffViewer_CompareMenuLabel=Compare with Previous &Version
CommitFileDiffViewer_CompareWorkingDirectoryMenuLabel=Compare with &Working Tree
-CommitFileDiffViewer_FileDoesNotExist=File {0} does not exist in the workspace
CommitFileDiffViewer_MergeCommitMultiAncestorMessage=This is a merge commit with more than one ancestor
-CommitFileDiffViewer_notContainedInCommit=File {0} is not contained in commit {1}
CommitFileDiffViewer_OpenInEditorMenuLabel=Open &This Version
CommitFileDiffViewer_OpenPreviousInEditorMenuLabel=Open &Previous Version
CommitFileDiffViewer_OpenWorkingTreeVersionInEditorMenuLabel=&Open Working Tree Version
StagingView_CompareWithIndexMenuLabel=Compare with Index
CommitFileDiffViewer_ShowAnnotationsMenuLabel=&Show Revision Information
CommitFileDiffViewer_ShowInHistoryLabel=Focus On This File
+DiffViewer_FileDoesNotExist=File {0} does not exist in the workspace
+DiffViewer_OpenComparisonLinkLabel=Open Side-by-Side Comparison
+DiffViewer_OpenWorkingTreeLinkLabel=Open Working Tree Version
+DiffViewer_OpenInEditorLinkLabel=Open This Version
+DiffViewer_OpenPreviousLinkLabel=Open Previous Version
+DiffViewer_notContainedInCommit=File {0} is not contained in commit {1}
CommitGraphTable_CommitId=Id
CommitGraphTable_Committer=Committer
CommitGraphTable_committerDataColumn=Committed Date

Back to the top