1 /*******************************************************************************
2 * Copyright (c) 2011, 2016 GitHub Inc. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License 2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-2.0/
8 * SPDX-License-Identifier: EPL-2.0
11 * Kevin Sawicki (GitHub Inc.) - initial API and implementation
12 * Tobias Pfeifer (SAP AG) - customizable font and color for the first header line - https://bugs.eclipse.org/397723
13 * Thomas Wolf <thomas.wolf@paranor.ch> - add hyperlinks, and use JFace syntax coloring
14 *******************************************************************************/
15 package org
.eclipse
.egit
.ui
.internal
.commit
;
17 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffAddBackgroundColor
;
18 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffAddForegroundColor
;
19 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffHeadlineBackgroundColor
;
20 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffHeadlineFont
;
21 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffHeadlineForegroundColor
;
22 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffHunkBackgroundColor
;
23 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffHunkForegroundColor
;
24 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffRemoveBackgroundColor
;
25 import static org
.eclipse
.egit
.ui
.UIPreferences
.THEME_DiffRemoveForegroundColor
;
28 import java
.io
.IOException
;
29 import java
.util
.ArrayList
;
30 import java
.util
.Arrays
;
31 import java
.util
.HashMap
;
32 import java
.util
.List
;
34 import java
.util
.function
.Supplier
;
35 import java
.util
.regex
.Matcher
;
36 import java
.util
.regex
.Pattern
;
38 import org
.eclipse
.compare
.ITypedElement
;
39 import org
.eclipse
.core
.resources
.IFile
;
40 import org
.eclipse
.core
.runtime
.Assert
;
41 import org
.eclipse
.core
.runtime
.CoreException
;
42 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
43 import org
.eclipse
.core
.runtime
.Path
;
44 import org
.eclipse
.egit
.core
.internal
.util
.ResourceUtil
;
45 import org
.eclipse
.egit
.ui
.Activator
;
46 import org
.eclipse
.egit
.ui
.internal
.CompareUtils
;
47 import org
.eclipse
.egit
.ui
.internal
.EgitUiEditorUtils
;
48 import org
.eclipse
.egit
.ui
.internal
.UIText
;
49 import org
.eclipse
.egit
.ui
.internal
.commit
.DiffRegionFormatter
.DiffRegion
;
50 import org
.eclipse
.egit
.ui
.internal
.commit
.DiffRegionFormatter
.FileDiffRegion
;
51 import org
.eclipse
.egit
.ui
.internal
.dialogs
.HyperlinkSourceViewer
;
52 import org
.eclipse
.egit
.ui
.internal
.history
.FileDiff
;
53 import org
.eclipse
.egit
.ui
.internal
.revision
.GitCompareFileRevisionEditorInput
;
54 import org
.eclipse
.jface
.preference
.IPreferenceStore
;
55 import org
.eclipse
.jface
.resource
.ColorRegistry
;
56 import org
.eclipse
.jface
.resource
.FontRegistry
;
57 import org
.eclipse
.jface
.resource
.JFaceResources
;
58 import org
.eclipse
.jface
.text
.BadLocationException
;
59 import org
.eclipse
.jface
.text
.Document
;
60 import org
.eclipse
.jface
.text
.IDocument
;
61 import org
.eclipse
.jface
.text
.IRegion
;
62 import org
.eclipse
.jface
.text
.ITextViewer
;
63 import org
.eclipse
.jface
.text
.ITypedRegion
;
64 import org
.eclipse
.jface
.text
.Region
;
65 import org
.eclipse
.jface
.text
.TextAttribute
;
66 import org
.eclipse
.jface
.text
.TextUtilities
;
67 import org
.eclipse
.jface
.text
.hyperlink
.AbstractHyperlinkDetector
;
68 import org
.eclipse
.jface
.text
.hyperlink
.IHyperlink
;
69 import org
.eclipse
.jface
.text
.hyperlink
.IHyperlinkDetector
;
70 import org
.eclipse
.jface
.text
.hyperlink
.IHyperlinkDetectorExtension2
;
71 import org
.eclipse
.jface
.text
.presentation
.IPresentationReconciler
;
72 import org
.eclipse
.jface
.text
.presentation
.PresentationReconciler
;
73 import org
.eclipse
.jface
.text
.rules
.DefaultDamagerRepairer
;
74 import org
.eclipse
.jface
.text
.rules
.IToken
;
75 import org
.eclipse
.jface
.text
.rules
.ITokenScanner
;
76 import org
.eclipse
.jface
.text
.rules
.Token
;
77 import org
.eclipse
.jface
.text
.source
.IOverviewRuler
;
78 import org
.eclipse
.jface
.text
.source
.ISourceViewer
;
79 import org
.eclipse
.jface
.text
.source
.IVerticalRuler
;
80 import org
.eclipse
.jface
.text
.source
.SourceViewerConfiguration
;
81 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
82 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
83 import org
.eclipse
.jgit
.annotations
.NonNull
;
84 import org
.eclipse
.jgit
.diff
.DiffEntry
;
85 import org
.eclipse
.jgit
.diff
.DiffEntry
.ChangeType
;
86 import org
.eclipse
.jgit
.lib
.ObjectId
;
87 import org
.eclipse
.jgit
.lib
.Repository
;
88 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
89 import org
.eclipse
.osgi
.util
.NLS
;
90 import org
.eclipse
.swt
.SWT
;
91 import org
.eclipse
.swt
.graphics
.Color
;
92 import org
.eclipse
.swt
.graphics
.Rectangle
;
93 import org
.eclipse
.swt
.widgets
.Composite
;
94 import org
.eclipse
.swt
.widgets
.Layout
;
95 import org
.eclipse
.team
.core
.history
.IFileRevision
;
96 import org
.eclipse
.ui
.IEditorPart
;
97 import org
.eclipse
.ui
.IWorkbenchPage
;
98 import org
.eclipse
.ui
.IWorkbenchWindow
;
99 import org
.eclipse
.ui
.PlatformUI
;
100 import org
.eclipse
.ui
.themes
.IThemeManager
;
103 * Source viewer to display one or more file differences using standard editor
104 * colors and fonts preferences. Should be used together with a
105 * {@link DiffDocument} to get proper coloring and hyperlink support.
107 public class DiffViewer
extends HyperlinkSourceViewer
{
109 private final Map
<String
, IToken
> tokens
= new HashMap
<>();
111 private final Map
<String
, Color
> backgroundColors
= new HashMap
<>();
113 private IPropertyChangeListener themeListener
= new IPropertyChangeListener() {
116 public void propertyChange(PropertyChangeEvent event
) {
117 String property
= event
.getProperty();
118 if (IThemeManager
.CHANGE_CURRENT_THEME
.equals(property
)
119 || THEME_DiffAddBackgroundColor
.equals(property
)
120 || THEME_DiffAddForegroundColor
.equals(property
)
121 || THEME_DiffHunkBackgroundColor
.equals(property
)
122 || THEME_DiffHunkForegroundColor
.equals(property
)
123 || THEME_DiffHeadlineBackgroundColor
.equals(property
)
124 || THEME_DiffHeadlineForegroundColor
.equals(property
)
125 || THEME_DiffHeadlineFont
.equals(property
)
126 || THEME_DiffRemoveBackgroundColor
.equals(property
)
127 || THEME_DiffRemoveForegroundColor
.equals(property
)) {
129 invalidateTextPresentation();
135 * A configuration to use with a {@link DiffViewer}, setting up the syntax
136 * coloring for a diff and adding the {@link IHyperlinkDetector} for the
139 public static class Configuration
140 extends HyperlinkSourceViewer
.Configuration
{
143 * Creates a new {@link Configuration} connected to the given
144 * {@link IPreferenceStore}.
146 * @param preferenceStore
149 public Configuration(IPreferenceStore preferenceStore
) {
150 super(preferenceStore
);
154 public int getHyperlinkStateMask(ISourceViewer sourceViewer
) {
159 protected IHyperlinkDetector
[] internalGetHyperlinkDetectors(
160 ISourceViewer sourceViewer
) {
161 Assert
.isTrue(sourceViewer
instanceof DiffViewer
);
162 IHyperlinkDetector
[] result
= { new HyperlinkDetector() };
167 public String
[] getConfiguredContentTypes(ISourceViewer sourceViewer
) {
168 Assert
.isTrue(sourceViewer
instanceof DiffViewer
);
169 DiffViewer viewer
= (DiffViewer
) sourceViewer
;
170 return viewer
.tokens
.keySet()
171 .toArray(new String
[viewer
.tokens
.size()]);
175 public IPresentationReconciler
getPresentationReconciler(
176 ISourceViewer sourceViewer
) {
177 Assert
.isTrue(sourceViewer
instanceof DiffViewer
);
178 DiffViewer viewer
= (DiffViewer
) sourceViewer
;
179 PresentationReconciler reconciler
= new PresentationReconciler();
180 reconciler
.setDocumentPartitioning(
181 getConfiguredDocumentPartitioning(viewer
));
182 for (String contentType
: viewer
.tokens
.keySet()) {
183 DefaultDamagerRepairer damagerRepairer
= new DefaultDamagerRepairer(
184 new SingleTokenScanner(
185 () -> viewer
.tokens
.get(contentType
)));
186 reconciler
.setDamager(damagerRepairer
, contentType
);
187 reconciler
.setRepairer(damagerRepairer
, contentType
);
194 * Creates a new {@link DiffViewer}.
197 * to contain the viewer
199 * for the viewer (left side)
203 public DiffViewer(Composite parent
, IVerticalRuler ruler
, int styles
) {
204 this(parent
, ruler
, null, false, styles
);
208 * Creates a new {@link DiffViewer}.
211 * to contain the viewer
213 * for the viewer (left side)
214 * @param overviewRuler
215 * ruler for overview annotations
216 * @param showsAnnotationOverview
217 * whether to show overview annotations
221 public DiffViewer(Composite parent
, IVerticalRuler ruler
,
222 IOverviewRuler overviewRuler
, boolean showsAnnotationOverview
,
224 super(parent
, ruler
, overviewRuler
, showsAnnotationOverview
, styles
);
225 getTextWidget().setAlwaysShowScrollBars(false);
227 setDocument(new Document());
230 .setFont(JFaceResources
.getFont(JFaceResources
.TEXT_FONT
));
235 protected void handleDispose() {
236 PlatformUI
.getWorkbench().getThemeManager()
237 .removePropertyChangeListener(themeListener
);
238 super.handleDispose();
242 public void configure(SourceViewerConfiguration config
) {
243 Assert
.isTrue(config
instanceof Configuration
);
244 super.configure(config
);
248 protected Layout
createLayout() {
249 return new FixedRulerLayout(GAP_SIZE_1
);
252 private class FixedRulerLayout
extends RulerLayout
{
254 public FixedRulerLayout(int gap
) {
259 protected void layout(Composite composite
, boolean flushCache
) {
260 Rectangle bounds
= composite
.getBounds();
261 if (bounds
.width
== 0 || bounds
.height
== 0) {
262 // The overview ruler is laid out wrongly in the DiffEditorPage:
263 // it ends up with a negative y-coordinate. This seems to be
264 // caused by layout attempts while the page is not visible,
265 // which cache that bogus negative offset in RulerLayout, which
266 // will re-use it even when the viewer is laid out again when
267 // the page is visible. So don't layout if the containing
268 // composite has no extent.
271 super.layout(composite
, flushCache
);
275 private void refreshDiffStyles() {
276 ColorRegistry col
= PlatformUI
.getWorkbench().getThemeManager()
277 .getCurrentTheme().getColorRegistry();
278 FontRegistry reg
= PlatformUI
.getWorkbench().getThemeManager()
279 .getCurrentTheme().getFontRegistry();
280 // We do the foreground via syntax coloring and the background via a
281 // line background listener. If we did the background also via the
282 // TextAttributes, this would take precedence over the line background
283 // resulting in strange display if the current line is highlighted:
284 // that highlighting would appear only beyond the end of the actual
285 // text content (i.e., beyond the end-of-line), while actual text
286 // would still get the background from the attribute.
287 tokens
.put(IDocument
.DEFAULT_CONTENT_TYPE
, new Token(null));
288 tokens
.put(DiffDocument
.HEADLINE_CONTENT_TYPE
,
289 new Token(new TextAttribute(
290 col
.get(THEME_DiffHeadlineForegroundColor
), null,
291 SWT
.NORMAL
, reg
.get(THEME_DiffHeadlineFont
))));
292 tokens
.put(DiffDocument
.HUNK_CONTENT_TYPE
, new Token(
293 new TextAttribute(col
.get(THEME_DiffHunkForegroundColor
))));
294 tokens
.put(DiffDocument
.ADDED_CONTENT_TYPE
, new Token(
295 new TextAttribute(col
.get(THEME_DiffAddForegroundColor
))));
296 tokens
.put(DiffDocument
.REMOVED_CONTENT_TYPE
, new Token(
297 new TextAttribute(col
.get(THEME_DiffRemoveForegroundColor
))));
298 backgroundColors
.put(DiffDocument
.HEADLINE_CONTENT_TYPE
,
299 col
.get(THEME_DiffHeadlineBackgroundColor
));
300 backgroundColors
.put(DiffDocument
.HUNK_CONTENT_TYPE
,
301 col
.get(THEME_DiffHunkBackgroundColor
));
302 backgroundColors
.put(DiffDocument
.ADDED_CONTENT_TYPE
,
303 col
.get(THEME_DiffAddBackgroundColor
));
304 backgroundColors
.put(DiffDocument
.REMOVED_CONTENT_TYPE
,
305 col
.get(THEME_DiffRemoveBackgroundColor
));
308 private void initListeners() {
309 PlatformUI
.getWorkbench().getThemeManager()
310 .addPropertyChangeListener(this.themeListener
);
311 getTextWidget().addLineBackgroundListener((event
) -> {
312 IDocument document
= getDocument();
313 if (document
instanceof DiffDocument
) {
315 // We are in SWT land here: we get widget offsets.
316 int modelOffset
= widgetOffset2ModelOffset(
318 ITypedRegion partition
= ((DiffDocument
) document
)
319 .getPartition(modelOffset
);
320 if (partition
!= null) {
321 Color color
= backgroundColors
.get(partition
.getType());
323 event
.lineBackground
= color
;
326 } catch (BadLocationException e
) {
334 protected void handleJFacePreferencesChange(PropertyChangeEvent event
) {
335 if (JFaceResources
.TEXT_FONT
.equals(event
.getProperty())) {
336 setFont(JFaceResources
.getFont(JFaceResources
.TEXT_FONT
));
338 super.handleJFacePreferencesChange(event
);
342 private static class SingleTokenScanner
implements ITokenScanner
{
344 private final Supplier
<IToken
> token
;
346 private int currentOffset
;
350 private int tokenStart
;
352 public SingleTokenScanner(Supplier
<IToken
> supplier
) {
353 this.token
= supplier
;
357 public void setRange(IDocument document
, int offset
, int length
) {
358 currentOffset
= offset
;
359 end
= offset
+ length
;
364 public IToken
nextToken() {
365 tokenStart
= currentOffset
;
366 if (currentOffset
< end
) {
374 public int getTokenOffset() {
379 public int getTokenLength() {
380 return currentOffset
- tokenStart
;
385 private static class HyperlinkDetector
extends AbstractHyperlinkDetector
386 implements IHyperlinkDetectorExtension2
{
388 private final Pattern HUNK_LINE_PATTERN
= Pattern
389 .compile("@@ ([-+]?(\\d+),\\d+) ([-+]?(\\d+),\\d+) @@"); //$NON-NLS-1$
392 public IHyperlink
[] detectHyperlinks(ITextViewer textViewer
,
393 IRegion region
, boolean canShowMultipleHyperlinks
) {
394 IDocument document
= textViewer
.getDocument();
395 if (!(document
instanceof DiffDocument
)
396 || document
.getLength() == 0) {
399 DiffDocument diffDocument
= (DiffDocument
) document
;
400 DiffRegion
[] regions
= diffDocument
.getRegions();
401 FileDiffRegion
[] fileRegions
= diffDocument
.getFileRegions();
402 if (regions
== null || regions
.length
== 0 || fileRegions
== null
403 || fileRegions
.length
== 0) {
406 int start
= region
.getOffset();
407 int end
= region
.getOffset() + region
.getLength();
408 DiffRegion key
= new DiffRegion(start
, 0);
409 int i
= Arrays
.binarySearch(regions
, key
, (a
, b
) -> {
410 if (a
.getOffset() > b
.getOffset() + b
.getLength()) {
413 if (a
.getOffset() + a
.getLength() < b
.getOffset()) {
418 List
<IHyperlink
> links
= new ArrayList
<>();
419 FileDiffRegion fileRange
= null;
420 for (; i
>= 0 && i
< regions
.length
; i
++) {
421 DiffRegion range
= regions
[i
];
422 if (range
.getOffset() >= end
) {
425 if (range
.getOffset() + range
.getLength() <= start
) {
428 // Range overlaps region
429 switch (range
.getType()) {
431 fileRange
= findFileRange(diffDocument
, fileRange
,
433 if (fileRange
!= null) {
434 DiffEntry
.ChangeType change
= fileRange
.getDiff()
441 if (getString(document
, range
.getOffset(),
442 range
.getLength()).startsWith("diff")) { //$NON-NLS-1$
443 // "diff" is at the beginning
444 IRegion linkRegion
= new Region(
445 range
.getOffset(), 4);
446 if (TextUtilities
.overlaps(region
,
448 links
.add(new CompareLink(linkRegion
,
457 fileRange
= findFileRange(diffDocument
, fileRange
,
459 if (fileRange
!= null) {
460 String line
= getString(document
, range
.getOffset(),
462 createHeaderLinks((DiffDocument
) document
, region
,
463 fileRange
, range
, line
, DiffEntry
.Side
.OLD
,
465 createHeaderLinks((DiffDocument
) document
, region
,
466 fileRange
, range
, line
, DiffEntry
.Side
.NEW
,
471 fileRange
= findFileRange(diffDocument
, fileRange
,
473 if (fileRange
!= null) {
474 String line
= getString(document
, range
.getOffset(),
476 Matcher m
= HUNK_LINE_PATTERN
.matcher(line
);
478 int lineOffset
= getContextLines(document
, range
,
479 i
+ 1 < regions
.length ? regions
[i
+ 1]
481 createHunkLinks(region
, fileRange
, range
, m
,
490 if (links
.isEmpty()) {
493 return links
.toArray(new IHyperlink
[links
.size()]);
496 private String
getString(IDocument document
, int offset
, int length
) {
498 return document
.get(offset
, length
);
499 } catch (BadLocationException e
) {
500 return ""; //$NON-NLS-1$
504 private int getContextLines(IDocument document
, DiffRegion hunk
,
508 switch (next
.getType()) {
510 int nofLines
= document
.getNumberOfLines(
511 next
.getOffset(), next
.getLength());
515 int hunkLine
= document
516 .getLineOfOffset(hunk
.getOffset());
517 int diffLine
= document
518 .getLineOfOffset(next
.getOffset());
519 return diffLine
- hunkLine
- 1;
523 } catch (BadLocationException e
) {
530 private FileDiffRegion
findFileRange(DiffDocument document
,
531 FileDiffRegion candidate
, int offset
) {
532 if (candidate
!= null && TextUtilities
.overlaps(candidate
,
533 new Region(offset
, 0))) {
536 return document
.findFileRegion(offset
);
539 private void createHeaderLinks(DiffDocument document
, IRegion region
,
540 FileDiffRegion fileRange
, DiffRegion range
, String line
,
541 @NonNull DiffEntry
.Side side
, List
<IHyperlink
> links
) {
542 Pattern p
= document
.getPathPattern(side
);
546 DiffEntry
.ChangeType change
= fileRange
.getDiff().getChange();
549 if (change
== DiffEntry
.ChangeType
.ADD
) {
554 if (change
== DiffEntry
.ChangeType
.DELETE
) {
560 Matcher m
= p
.matcher(line
);
562 IRegion linkRegion
= new Region(range
.getOffset() + m
.start(),
563 m
.end() - m
.start());
564 if (TextUtilities
.overlaps(region
, linkRegion
)) {
565 if (side
== DiffEntry
.Side
.NEW
) {
566 File file
= new Path(fileRange
.getRepository()
567 .getWorkTree().getAbsolutePath()).append(
568 fileRange
.getDiff().getNewPath())
571 links
.add(new FileLink(linkRegion
, file
, -1));
574 links
.add(new OpenLink(linkRegion
, fileRange
, side
, -1));
579 private void createHunkLinks(IRegion region
, FileDiffRegion fileRange
,
580 DiffRegion range
, Matcher m
, int lineOffset
,
581 List
<IHyperlink
> links
) {
582 DiffEntry
.ChangeType change
= fileRange
.getDiff().getChange();
583 if (change
!= DiffEntry
.ChangeType
.ADD
) {
584 IRegion linkRegion
= new Region(range
.getOffset() + m
.start(1),
585 m
.end(1) - m
.start(1));
586 if (TextUtilities
.overlaps(linkRegion
, region
)) {
587 int lineNo
= Integer
.parseInt(m
.group(2)) - 1 + lineOffset
;
588 if (change
!= DiffEntry
.ChangeType
.DELETE
) {
590 new CompareLink(linkRegion
, fileRange
, lineNo
));
592 links
.add(new OpenLink(linkRegion
, fileRange
,
593 DiffEntry
.Side
.OLD
, lineNo
));
596 if (change
!= DiffEntry
.ChangeType
.DELETE
) {
597 IRegion linkRegion
= new Region(range
.getOffset() + m
.start(3),
598 m
.end(3) - m
.start(3));
599 if (TextUtilities
.overlaps(linkRegion
, region
)) {
600 int lineNo
= Integer
.parseInt(m
.group(4)) - 1 + lineOffset
;
601 if (change
!= DiffEntry
.ChangeType
.ADD
) {
603 new CompareLink(linkRegion
, fileRange
, lineNo
));
605 File file
= new Path(fileRange
.getRepository().getWorkTree()
607 .append(fileRange
.getDiff().getNewPath())
610 links
.add(new FileLink(linkRegion
, file
, lineNo
));
612 links
.add(new OpenLink(linkRegion
, fileRange
,
613 DiffEntry
.Side
.NEW
, lineNo
));
619 public int getStateMask() {
624 private static abstract class RevealLink
implements IHyperlink
{
626 private final IRegion region
;
628 protected final int lineNo
;
630 protected RevealLink(IRegion region
, int lineNo
) {
631 this.region
= region
;
632 this.lineNo
= lineNo
;
636 public IRegion
getHyperlinkRegion() {
641 public String
getTypeLabel() {
647 private static class FileLink
extends RevealLink
{
649 private final File file
;
651 public FileLink(IRegion region
, File file
, int lineNo
) {
652 super(region
, lineNo
);
657 public String
getHyperlinkText() {
658 return UIText
.DiffViewer_OpenWorkingTreeLinkLabel
;
663 openFileInEditor(file
, lineNo
);
668 private static class CompareLink
extends RevealLink
{
670 protected final Repository repository
;
672 protected final FileDiff fileDiff
;
674 public CompareLink(IRegion region
, FileDiffRegion fileRange
,
676 super(region
, lineNo
);
677 this.repository
= fileRange
.getRepository();
678 this.fileDiff
= fileRange
.getDiff();
682 public String
getHyperlinkText() {
683 return UIText
.DiffViewer_OpenComparisonLinkLabel
;
688 // No way to selectAndReveal a line or a diff node in a
690 showTwoWayFileDiff(repository
, fileDiff
);
695 private static class OpenLink
extends CompareLink
{
697 private final DiffEntry
.Side side
;
699 public OpenLink(IRegion region
, FileDiffRegion fileRange
,
700 DiffEntry
.Side side
, int lineNo
) {
701 super(region
, fileRange
, lineNo
);
706 public String
getHyperlinkText() {
709 return UIText
.DiffViewer_OpenPreviousLinkLabel
;
711 return UIText
.DiffViewer_OpenInEditorLinkLabel
;
717 openInEditor(repository
, fileDiff
, side
, lineNo
);
723 * Opens the file, if it exists, in an editor.
727 * @param lineNoToReveal
728 * if >= 0, select and reveals the given line
730 public static void openFileInEditor(File file
, int lineNoToReveal
) {
731 if (!file
.exists()) {
733 NLS
.bind(UIText
.DiffViewer_FileDoesNotExist
,
738 IWorkbenchPage page
= PlatformUI
.getWorkbench()
739 .getActiveWorkbenchWindow().getActivePage();
740 IEditorPart editor
= EgitUiEditorUtils
.openEditor(file
, page
);
741 EgitUiEditorUtils
.revealLine(editor
, lineNoToReveal
);
745 * Opens either the new or the old version of a {@link FileDiff} in an
749 * the {@link FileDiff} belongs to
751 * the {@link FileDiff}
754 * @param lineNoToReveal
755 * if >= 0, select and reveals the given line
757 public static void openInEditor(Repository repository
, FileDiff d
,
758 DiffEntry
.Side side
, int lineNoToReveal
) {
759 ObjectId
[] blobs
= d
.getBlobs();
762 openInEditor(repository
, d
.getOldPath(), d
.getCommit().getParent(0),
763 blobs
[0], lineNoToReveal
);
766 openInEditor(repository
, d
.getNewPath(), d
.getCommit(),
767 blobs
[blobs
.length
- 1], lineNoToReveal
);
772 private static void openInEditor(Repository repository
, String path
,
773 RevCommit commit
, ObjectId blob
, int reveal
) {
775 IFileRevision rev
= CompareUtils
.getFileRevision(path
, commit
,
778 String message
= NLS
.bind(
779 UIText
.DiffViewer_notContainedInCommit
, path
,
781 Activator
.showError(message
, null);
784 IWorkbenchWindow window
= PlatformUI
.getWorkbench()
785 .getActiveWorkbenchWindow();
786 IWorkbenchPage page
= window
.getActivePage();
787 IEditorPart editor
= EgitUiEditorUtils
.openEditor(page
, rev
,
788 new NullProgressMonitor());
789 EgitUiEditorUtils
.revealLine(editor
, reveal
);
790 } catch (IOException
| CoreException e
) {
791 Activator
.handleError(UIText
.GitHistoryPage_openFailed
, e
, true);
796 * Shows a two-way diff between the old and new versions of a
797 * {@link FileDiff} in a compare editor.
800 * the {@link FileDiff} belongs to
802 * the {@link FileDiff} to show
804 public static void showTwoWayFileDiff(Repository repository
, FileDiff d
) {
805 String np
= d
.getNewPath();
806 String op
= d
.getOldPath();
807 RevCommit c
= d
.getCommit();
808 ObjectId
[] blobs
= d
.getBlobs();
811 final RevCommit oldCommit
;
812 final ObjectId oldObjectId
;
813 if (!d
.getChange().equals(ChangeType
.ADD
)) {
814 oldCommit
= c
.getParent(0);
815 oldObjectId
= blobs
[0];
822 final RevCommit newCommit
;
823 final ObjectId newObjectId
;
824 if (d
.getChange().equals(ChangeType
.DELETE
)) {
829 newObjectId
= blobs
[blobs
.length
- 1];
831 IWorkbenchWindow window
= PlatformUI
.getWorkbench()
832 .getActiveWorkbenchWindow();
833 IWorkbenchPage page
= window
.getActivePage();
834 if (oldCommit
!= null && newCommit
!= null && repository
!= null) {
835 IFile file
= np
!= null
836 ? ResourceUtil
.getFileForLocation(repository
, np
, false)
840 CompareUtils
.compare(file
, repository
, np
, op
,
841 newCommit
.getName(), oldCommit
.getName(), false,
844 CompareUtils
.compareBetween(repository
, np
, op
,
845 newCommit
.getName(), oldCommit
.getName(),
848 } catch (IOException e
) {
849 Activator
.handleError(UIText
.GitHistoryPage_openFailed
, e
,
855 // still happens on initial commits
856 final ITypedElement oldSide
= createTypedElement(repository
, op
,
857 oldCommit
, oldObjectId
);
858 final ITypedElement newSide
= createTypedElement(repository
, np
,
859 newCommit
, newObjectId
);
860 CompareUtils
.openInCompare(page
,
861 new GitCompareFileRevisionEditorInput(newSide
, oldSide
, null));
864 private static ITypedElement
createTypedElement(Repository repository
,
865 String path
, final RevCommit commit
, final ObjectId objectId
) {
866 if (null != commit
) {
867 return CompareUtils
.getFileRevisionTypedElement(path
, commit
,
868 repository
, objectId
);
870 return new GitCompareFileRevisionEditorInput
.EmptyTypedElement(""); //$NON-NLS-1$