This is most likely the underlying cause of #5181 and #4940, and reproduces with:
After step 4 the unsaved changes from step 2 are removed.
The problem is with the MergeBuffer method which intends to do a 3-way merge between the last known state of the editor, the incoming file changes, and the current state of the text document.
There is no real built-in support for doing a 3-way merge and most of the examples of using diff are deeply buried in other code.
For doing a merge -- which is really what you are doing here -- it might be best to talk to the TFS people to see what algorithms they are using. You can fudge things to an extent:
- Given a common version V1
- A set of user edits that turn V1 -> V1e and
- A set of project changes that turn V1 -> v1p
IIRC, CPS is using diff to compute the changes from V1 -> V1e and V1 -> v1p and then use a LiveShare-like algorithm to resolve the conflicts. I can help you with improving that but I suspect you may end up doing better with either some customized merging logic from TFS or by looking at the user edits and translating them into some action that CPS understands (e.g. add an attribute).
Spent some time digging in to this. For posterity:
The current algorithm works like this:
Project File: Foo Bar
User Edit : Frob Bar
File change 1, Adding Baz:
Base snapshot: Foo Bar
Project file : Foo Bar Baz
Text buffer : Frob Bar
Diff between base and file: Added Baz
Result after applying diff to text buffer: Frob Bar Baz
File change 2, Adding Yolo:
Base snapshot: Frob Bar Baz
Project file : Foo Bar Baz Yolo
Text buffer : Frob Bar Baz
Diff between base and file: Change Frob to Foo, Added Yolo
Result after applying diff to text buffer: Foo Bar Baz Yolo
As can be seen, because the algorithm prioritises the project file, the change to Foo is actually reversed because the algorithm thinks the change has been applied to the project file.
The logical thing to do here is not update the base snapshot after every change, but if we update the code to only update the baseline when the file is saved, leaving it more similar to the project file:
Project File: Foo Bar
User Edit : Frob Bar
File change 1, Adding Baz:
Base snapshot: Foo Bar
Project file : Foo Bar Baz
Text buffer : Frob Bar
Diff between base and file: Added Baz
Result after applying diff to text buffer: Frob Bar Baz
File change 2, Adding Yolo:
Base snapshot: Foo Bar
Project file : Foo Bar Baz Yolo
Text buffer : Frob Bar Baz
Diff between base and file: Added Baz Yolo
Result after applying diff to text buffer: Frob Bar Baz Baz Yolo
As we can see, now the Foo change is left alone, but each individual addition is seen as adding the whole set.
The strategy I'm working on that looks promising is essentially:
Given a common base V1, a user edited version of that V1e, and the project file contents V1p:
The above works fine if user edits are made above any changes to the project file. If they're below then it fails because there is no way to know where the user edits should be after inserting the project changes. :(
PR complete.