2121import java .io .InputStream ;
2222import java .lang .reflect .InvocationTargetException ;
2323import java .net .URL ;
24+ import java .nio .charset .StandardCharsets ;
2425import java .util .ArrayList ;
26+ import java .util .Arrays ;
2527import java .util .Collection ;
2628import java .util .Collections ;
2729import java .util .Comparator ;
4345import org .eclipse .compare .CompareConfiguration ;
4446import org .eclipse .compare .CompareEditorInput ;
4547import org .eclipse .compare .IResourceProvider ;
48+ import org .eclipse .compare .ISharedDocumentAdapter ;
4649import org .eclipse .compare .IStreamContentAccessor ;
4750import org .eclipse .compare .IStreamMerger ;
4851import org .eclipse .compare .ITypedElement ;
4952import org .eclipse .compare .internal .core .CompareSettings ;
53+ import org .eclipse .compare .internal .merge .DocumentMerger .IDocumentMergerInput ;
5054import org .eclipse .compare .structuremergeviewer .ICompareInput ;
5155import org .eclipse .compare .structuremergeviewer .IStructureCreator ;
56+ import org .eclipse .compare .structuremergeviewer .SharedDocumentAdapterWrapper ;
5257import org .eclipse .compare .structuremergeviewer .StructureDiffViewer ;
58+ import org .eclipse .compare .unifieddiff .UnifiedDiff ;
59+ import org .eclipse .compare .unifieddiff .UnifiedDiffMode ;
60+ import org .eclipse .compare .unifieddiff .internal .CancelAllRunnable ;
5361import org .eclipse .core .resources .IFile ;
5462import org .eclipse .core .resources .IResource ;
5563import org .eclipse .core .runtime .Adapters ;
6270import org .eclipse .core .runtime .IPath ;
6371import org .eclipse .core .runtime .IProgressMonitor ;
6472import org .eclipse .core .runtime .IStatus ;
73+ import org .eclipse .core .runtime .NullProgressMonitor ;
6574import org .eclipse .core .runtime .OperationCanceledException ;
6675import org .eclipse .core .runtime .Platform ;
6776import org .eclipse .core .runtime .Status ;
6877import org .eclipse .core .runtime .content .IContentDescription ;
6978import org .eclipse .core .runtime .content .IContentType ;
7079import org .eclipse .core .runtime .content .IContentTypeManager ;
80+ import org .eclipse .jface .action .Action ;
7181import org .eclipse .jface .dialogs .MessageDialog ;
7282import org .eclipse .jface .operation .IRunnableContext ;
7383import org .eclipse .jface .preference .IPreferenceStore ;
8494import org .eclipse .ui .IEditorInput ;
8595import org .eclipse .ui .IEditorPart ;
8696import org .eclipse .ui .IEditorRegistry ;
97+ import org .eclipse .ui .IFileEditorInput ;
8798import org .eclipse .ui .IReusableEditor ;
8899import org .eclipse .ui .ISharedImages ;
89100import org .eclipse .ui .IWorkbench ;
90101import org .eclipse .ui .IWorkbenchPage ;
91102import org .eclipse .ui .IWorkbenchWindow ;
92103import org .eclipse .ui .PartInitException ;
93104import org .eclipse .ui .PlatformUI ;
105+ import org .eclipse .ui .ide .IDE ;
94106import org .eclipse .ui .model .IWorkbenchAdapter ;
107+ import org .eclipse .ui .part .MultiPageEditorPart ;
95108import org .eclipse .ui .plugin .AbstractUIPlugin ;
109+ import org .eclipse .ui .texteditor .ITextEditor ;
96110import org .osgi .framework .BundleContext ;
97111import org .osgi .framework .ServiceRegistration ;
98112
@@ -560,6 +574,10 @@ public void openCompareEditor(final CompareEditorInput input,
560574 CompareConfiguration configuration = input .getCompareConfiguration ();
561575 if (configuration != null ) {
562576 IPreferenceStore ps = configuration .getPreferenceStore ();
577+ boolean unifiedDiffEnabled = ps .getBoolean (ComparePreferencePage .UNIFIED_DIFF );
578+ if (unifiedDiffEnabled && openUnifiedDiffInEditor (input , page , editor , activate )) {
579+ return ;
580+ }
563581 if (ps != null ) {
564582 configuration .setProperty (
565583 CompareConfiguration .USE_OUTLINE_VIEW ,
@@ -575,9 +593,199 @@ public void openCompareEditor(final CompareEditorInput input,
575593 }
576594 }
577595
578- private void openEditorInBackground (final CompareEditorInput input ,
579- final IWorkbenchPage page , final IReusableEditor editor ,
580- final boolean activate ) {
596+ private boolean openUnifiedDiffInEditor (final CompareEditorInput input , final IWorkbenchPage page ,
597+ IReusableEditor editor , boolean activate ) {
598+ var unifiedDiffInput = canShowInUnifiedDiff (input );
599+ if (unifiedDiffInput !=null ) {
600+ try {
601+ IWorkbenchPage wpage = page != null ? page : getActivePage ();
602+ if (unifiedDiffInput .left instanceof IFileEditorInput ) {
603+ IEditorPart leftEditor = wpage .openEditor (unifiedDiffInput .left ,
604+ getEditorId (unifiedDiffInput .left , unifiedDiffInput .leftElement ));
605+ if (leftEditor instanceof MultiPageEditorPart mpe ) {
606+ Object p = mpe .getSelectedPage ();
607+ if (p instanceof IEditorPart ) {
608+ leftEditor = (IEditorPart ) p ;
609+ }
610+ }
611+ if (leftEditor instanceof ITextEditor leftTextEditor ) {
612+ Action openTwoWayCompare = createOpenTwoWayCompareAction (input , page , editor , activate ,
613+ leftTextEditor );
614+ IStatus status = UnifiedDiff
615+ .create (leftTextEditor , getSourceOf (unifiedDiffInput .rightAcessor ()),
616+ UnifiedDiffMode .REVERT_MODE )
617+ .additionalActions (Arrays .asList (openTwoWayCompare ))
618+ .ignoreWhitespaceContributorFactory (t -> unifiedDiffInput .documentMergerInput != null
619+ ? unifiedDiffInput .documentMergerInput .createIgnoreWhitespaceContributor (t )
620+ : Optional .empty ())
621+ .tokenComparatorFactory (t -> unifiedDiffInput .documentMergerInput != null
622+ ? unifiedDiffInput .documentMergerInput .createTokenComparator (t )
623+ : null )
624+ .ignoreWhiteSpace (Utilities .getBoolean (input .getCompareConfiguration (),
625+ CompareConfiguration .IGNORE_WHITESPACE , false ))
626+ .open ();
627+ return status .isOK ();
628+ }
629+ } else {
630+ IEditorPart rightEditor = wpage .openEditor (unifiedDiffInput .right ,
631+ getEditorId (unifiedDiffInput .right , unifiedDiffInput .rightElement ));
632+ if (rightEditor instanceof MultiPageEditorPart mpe ) {
633+ Object p = mpe .getSelectedPage ();
634+ if (p instanceof IEditorPart ) {
635+ rightEditor = (IEditorPart ) p ;
636+ }
637+ }
638+ if (rightEditor instanceof ITextEditor rightTextEditor ) {
639+ Action openTwoWayCompare = createOpenTwoWayCompareAction (input , page , editor , activate ,
640+ rightTextEditor );
641+ IStatus status = UnifiedDiff
642+ .create (rightTextEditor , getSourceOf (unifiedDiffInput .leftAcessor ()),
643+ UnifiedDiffMode .OVERLAY_READ_ONLY_MODE )
644+ .additionalActions (Arrays .asList (openTwoWayCompare ))
645+ .ignoreWhitespaceContributorFactory (t -> unifiedDiffInput .documentMergerInput != null
646+ ? unifiedDiffInput .documentMergerInput .createIgnoreWhitespaceContributor (t )
647+ : Optional .empty ())
648+ .tokenComparatorFactory (t -> unifiedDiffInput .documentMergerInput != null
649+ ? unifiedDiffInput .documentMergerInput .createTokenComparator (t )
650+ : null )
651+ .ignoreWhiteSpace (Utilities .getBoolean (input .getCompareConfiguration (),
652+ CompareConfiguration .IGNORE_WHITESPACE , false ))
653+ .open ();
654+ return status .isOK ();
655+ }
656+ }
657+ } catch (PartInitException e ) {
658+ CompareUIPlugin .log (e );
659+ }
660+ }
661+ return false ;
662+ }
663+
664+ private Action createOpenTwoWayCompareAction (final CompareEditorInput input , final IWorkbenchPage page ,
665+ IReusableEditor editor , boolean activate , ITextEditor textEditor ) {
666+ ImageDescriptor image = CompareUIPlugin .getImageDescriptor (ICompareUIConstants .TWO_WAY_COMPARE );
667+ Action openTwoWayCompare = new Action (null , image ) {
668+ @ Override
669+ public void run () {
670+ new CancelAllRunnable (textEditor ).run ();
671+ if (input .canRunAsJob ()) {
672+ openEditorInBackground (input , page , editor , activate );
673+ } else {
674+ if (compareResultOK (input , null )) {
675+ internalOpenEditor (input , page , editor , activate );
676+ }
677+ }
678+ }
679+ };
680+ openTwoWayCompare .setToolTipText ("Open in 2-way Compare Editor" );
681+ return openTwoWayCompare ;
682+ }
683+
684+ private String getSourceOf (IStreamContentAccessor right ) {
685+ try {
686+ if (right == null ) {
687+ return "" ; //$NON-NLS-1$
688+ }
689+ String result = toString (right .getContents ());
690+ return result ;
691+ } catch (CoreException | IOException e ) {
692+ CompareUIPlugin .log (e );
693+ }
694+ return null ;
695+ }
696+
697+ private static String toString (InputStream inputStream ) throws IOException {
698+ if (inputStream == null ) {
699+ return "" ; //$NON-NLS-1$
700+ }
701+ try (inputStream ) {
702+ return new String (inputStream .readAllBytes (), StandardCharsets .UTF_8 );
703+ }
704+ }
705+
706+ private static String getEditorId (IEditorInput editorInput , ITypedElement element ) {
707+ String fileName = editorInput .getName ();
708+ IEditorRegistry registry = PlatformUI .getWorkbench ().getEditorRegistry ();
709+ IContentType type = getContentType (element );
710+ IEditorDescriptor descriptor = registry .getDefaultEditor (fileName , type );
711+ IDE .overrideDefaultEditorAssociation (editorInput , type , descriptor );
712+ String id ;
713+ if (descriptor == null || descriptor .isOpenExternal ()) {
714+ id = "org.eclipse.ui.DefaultTextEditor" ; //$NON-NLS-1$
715+ } else {
716+ id = descriptor .getId ();
717+ }
718+ return id ;
719+ }
720+
721+ private static record LeftEditorInputAndRightStreamContentAccessor (IEditorInput left , ITypedElement leftElement ,
722+ IStreamContentAccessor leftAcessor , IEditorInput right , ITypedElement rightElement ,
723+ IStreamContentAccessor rightAcessor , IDocumentMergerInput documentMergerInput ) {
724+ }
725+
726+ private LeftEditorInputAndRightStreamContentAccessor canShowInUnifiedDiff (CompareEditorInput input ) {
727+ IDocumentMergerInput documentMergerInput = null ;
728+ try {
729+ input .run (new NullProgressMonitor ());
730+ Object res = input .getCompareResult ();
731+ if (!(res instanceof ICompareInput compareInput )) {
732+ return null ;
733+ }
734+ ITypedElement ancestor = compareInput .getAncestor ();
735+ if (ancestor != null ) {
736+ return null ;
737+ }
738+ // TODO (tm) do not support if one side is editable?
739+ ITypedElement left = compareInput .getLeft ();
740+ if (left == null ) {
741+ return null ;
742+ }
743+ ISharedDocumentAdapter leftSda = SharedDocumentAdapterWrapper .getAdapter (left );
744+ if (leftSda == null ) {
745+ return null ;
746+ }
747+ IEditorInput leftEditorInput = leftSda .getDocumentKey (left );
748+ if (leftEditorInput == null ) {
749+ return null ;
750+ }
751+ if (!(left instanceof IStreamContentAccessor leftSa )) {
752+ return null ;
753+ }
754+ ITypedElement right = compareInput .getRight ();
755+ if (right == null ) {
756+ return null ;
757+ }
758+ ISharedDocumentAdapter rightSda = SharedDocumentAdapterWrapper .getAdapter (right );
759+ if (rightSda == null ) {
760+ return null ;
761+ }
762+ IEditorInput rightEditorInput = rightSda .getDocumentKey (right );
763+ if (rightEditorInput == null ) {
764+ return null ;
765+ }
766+ if (!(right instanceof IStreamContentAccessor rightSa )) {
767+ return null ;
768+ }
769+ var invisibleParent = new Shell ();
770+ try {
771+ invisibleParent .setVisible (false );
772+ var viewer = input .findContentViewer (new NullViewer (getShell ()), compareInput , invisibleParent );
773+ if (viewer instanceof IAdaptable adaptable ) {
774+ documentMergerInput = adaptable .getAdapter (IDocumentMergerInput .class );
775+ }
776+ } finally {
777+ invisibleParent .dispose ();
778+ }
779+ return new LeftEditorInputAndRightStreamContentAccessor (leftEditorInput , left , leftSa , rightEditorInput ,
780+ right , rightSa , documentMergerInput );
781+ } catch (InvocationTargetException | InterruptedException e ) {
782+ CompareUIPlugin .log (e );
783+ }
784+ return null ;
785+ }
786+
787+ private void openEditorInBackground (final CompareEditorInput input , final IWorkbenchPage page ,
788+ final IReusableEditor editor , final boolean activate ) {
581789 internalOpenEditor (input , page , editor , activate );
582790 }
583791
0 commit comments