This page captures some of the enhancements that were made to the Compare framework starting in the 3.3 release and continuing into 3.4.
Support for File Buffers
Eclipse has support for file buffers which are used to allow multiple editors to share the same document. When editors share the same document, changes made in one editor are reflected in the other. The following changes were made in 3.3 to support the use of file buffers in Compare editors:
- The ISharedDocumentAdapter interface was defined as API. The interface provides an extension to an ITypedElement that viewers can use to access a shared document (i.e. synchronized file buffer) for a types element.
- TextMergeViewer has been modified to make use of a shared document if a typed element being edited supplies one and the TextMergeViewer or it's subclass support it. In order to support shared documents, the subclass must use the default content provider and must provide a document partitioning if it makes use of a document document partitioner. In other words, the subclass must indicate the type of the partitioner since the default type cannot be sed for shared documents.
- StructureDiffViewer has been updated to support shared documents and an IStructureCreator2 interface has been defined along with a StructureCreator abstract class that provides a default implementation that manages shared documents for an element. Like TextMergeViewer, subclasses of StructureCreator must provide a document partitioning if they provide a document partitioner.
- The structure creator must also adapt the result returned from createStructure to a shared document adapter that wraps the adapter of the input. Ths is require to allow the viewers to detect document changes. It can use the SharedDocumentAdapterWrapper to do this and must make sure it returns the proper codument key for the original input (see bug 162561 for a request to simplify this).
Currently, the compare editor input is prepared in the foreground before the editor is opened. Given that this operation may involve server round-trips and difference calculations, it would be bnefitial if it could be done in the background.
Saving properly in the Compare framework has always been a bit of a black art. See bug 125538 for a description of one way of making it work. The reason for this is the layered nature of the Compare framework. For a local file, the layers are:
- The file (instance of IFile)
- The ResourceNode (instance of IEditableContent)
- The TextMergeViewer
- The CompareEditorInput
- The CompareEditor or CompareDialog
Assume you have an merge viewer open and edit the left side. A save is triggered by a Save operation or selecting another element to edit. What happens in this case is the following:
- Save is called on ContentMergeViewer
- Save obtains the content from the left side of the content merge viewer and passes it to the viewer content provider (instance of MergeViewerContentProvider).
- The content provider calls saveContent on the left typed element of the compare input (if the compare input is an IEditableContent).
- the ResourceNode updates its buffer to contain the new content and fire a change event
- if you want the file to be saved, you need to listen to that event and call commit on the ResourceDiffNode. For Team, we implement the save behavior in a couple of places. In the old-style syncs, it was a subclass of CompareEditorInput (SyncInfoCompareInput) that listened to the contet change and performed the commit (i.e. the method on ResourceNode that saves). For the new-style sync, we have a class associated with the resource (ResourceSaveableComparison) that performs the save.
There are a couple of observations that can be made about this:
- The ContentMergeViewer portion of the above only works with byte oriented content.
- Both the document in the TextMergeViewer and the ResourceNode buffer the contents of the file (that means there are at least two in-memory versions of the file for each side (and the ancestor if there is one).
- the save method of ContentMergeViewer is really a flush.
- However most client (e.g. Team Synchronize) treat the flush as an indication that a save should occur.
In 3.3 M3, a SaveableCompareEditorInput was released as API. This class has helper methds and special handling for diff nodes that represent a local file. This special handling includes the use of file buffers and a save to disk whenever the viewer is flushed.
There are a few other issues to consider here:
- Implementors of custom ContentMergeViewers need to be able to participate in the saving of ContentMergeViewers (see bug 143852)
- The Saveable API was introduced in Eclipse 3.2 and could potentially be used by merge viewers. The new-style model-based sync makes use of this API already.
Here are some thoughts on how this could work.
- Each CompareInput may have one or more Saveables associated with them.
- The CompareEditorInput would keep track of what Saveables it contained and would keep the workbench view of them up-to-date.
- If the ContentMergeViewer or subclass wants to save, they can flush and then invoke save on the appropriate Saveables (i.e. it may only be a subset of the Saveables in the CompareEditorInput).
- If the editor is saved, again, a flush is required before the save is issued to all dirty saveables of the CompareEditorInput.
- Of course, backwards compatibility must be maintained.
More invetsigation is required to determine if any work is feasible for 3.3.
When opening a Text or Java comparison from the Synchronize view, there are three sets of up/down navigation buttons that will appear: one set in the Synchronize view, one set in the toolbar and one set in the merge viewer. Each set of buttons has slightly different behavior.
- The toolbar buttons will cycle through the differences in the Content Compare pane and then cycle through the entries in the Structure Compare pane.
- The viewer buttons will cycle through each individual change in the Content Compare pane.
- The Synchronize view buttons will do what the toolbar buttons do but go to the next/previous element in the Synchronize view when the end/beginning is reached.
It would be good if we could condense this into a consistent unified navigation behavior.
There are several package private methods on ContentMergeViewer that would be helpful for subclasses to be able to override. A case in point is the TextMergeViewer that does this already because it is in the same package (see bug 144638). In 3.3 M3, changes were made to ContentMergeViewer to surface as API the methods that were required by TextMergeViewer.