OpenedFile / FileModels refactoring #303

dgrunwald opened this Issue Jan 27, 2014 · 0 comments


None yet

2 participants


Originally, view contents in SharpDevelop were loading/saving files on their own.
But this was causing problems when the same file was open in multiple editors - e.g. resource editor and text editor, or hex editor and text editor.
To solve this problem, the file content needs to be synchronized between the different views.
I implemented this way back in SharpDevelop 3.0 Beta 2: "SD-1234: Create common way to handle in-memory representations of files that have multiple views"
I'll use "SD-1234" as a shorthand to refer to the current system.

When I implemented SD-1234, the IViewContent interface was changed to put SharpDevelop in control of when files are saved/loaded (previously, the view content was in control).
While this solves the problem from SharpDevelop's point of view, it has made it very difficult to correctly implement the IViewContent interface, in particular when a view content needs to edit multiple files.
The SD-1234 design prevents view contents from deciding when to load, and forces them to load each file individually - it's very hard to implement a load operation that reads from multiple files, but that's what most multi-file view contents need (for example, the forms designer).

Also, loading/saving a higher-level representation of the data really shouldn't be the job of the view content - we should separate model from view!

Here is a new idea that I've been considering for quite some while:

  • We remove IViewContent.Load(); view contents will again decide on their own when to load.
  • We keep the "multiple views of the same file" feature.
    Indeed, my idea improves on it -- multiple text editors will share the same underlying TextDocument, so multiple views can act as a replacement for the
    "Split view" feature (which got removed in SD 5.0)
  • SharpDevelop gets to keep deciding when to save (though as with SD-1234, the view content is free to call openedFile.SaveToDisk())
    [though in the design below, a view content can also decide to save to a lower-level model (e.g. forms designer to textdocument) without going all the way to disk/memory stream]

The basic idea is that instead of synchronizing multiple views by saving/loading through MemoryStreams, we will synchronize them by making them share the underlying model. If views use different kinds of models, the save/load functionality of the models will be used to convert from one type of model to the other.
With my new API idea, pretty much anything can be used as a model. In particular, we can use the AvalonEdit TextDocument class as-is without wrapping it in a 'IDocumentModel' type or similar.

API idea (from view content PoV):
In the view content ctor, the view content just requests a model from the opened file:
TextDocument document = file.GetModel(FileModels.Document);
If the file wasn't open before, this will load the document from disk. (possibly throwing an exception)
If the file is open in another text editor, this will reuse the existing TextDocument.
If the file is open in a non-text editor (e.g. HexEditor), this will load the document from that editor.

If the view content needs multiple files, it can grab several models directly in the viewcontent ctor -- no more waiting for several Load() calls and ForceInitializeView() hacks.

In the case where the file is open in multiple different views (e.g. text and hex editor), we will have multiple models that can potentially be modified
independently of each other.
This is intentional: the conversion between different model types is potentially expensive, so we can't perform it on every change.
If the two views are open side-by-side, the user is necessarily going to see stale data in one of them.
In SD-1234, this problem is solved by converting whenever the user switches focus between the view contents. (Load() operation triggered by SharpDevelop)
This is no longer possible in the new design, as I want the view contents to be in control of loads.
My solution is simple: view contents should call GetModel() again whenever they receive focus, to ensure they get a non-stale model.

When saving, changes should be directly written into the model; and the model implementation is responsible of notifying the OpenedFile of the changes
(so that the dirty flag can be set, and other models are invalidated)

@dgrunwald dgrunwald modified the milestone: 5.0 Beta 3, 5.0 Beta 2 Feb 19, 2014
@siegfriedpammer siegfriedpammer modified the milestone: 5.1, 5.0 Beta 3 Mar 18, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment