Preserving intention in collaborative editing by special-casing empty lines
This post mainly outlines the user-centric view for a broken workflow which can occur in collaborative editing.
It does not go into the intricacies of implementing a solution as that will look different depending on the data model and the conflict resolution approach used.
Motivation: Concurrent inserts🔗
Imagine we have a collaborative editor with support for concurrent edits.
An initial text file usually consists of an empty line.
The two users u_0 and u_1 have agreed to use a text file as a shopping list, with one item per line. Concurrently, they come up with two ideas and both enter them into the doc.
u_0's doc is:
salt
while u_1's doc is:
spaghetti
After syncing, most existing editors will come up with something like this:
saltspaghetti
None of the users' intentions: "have a line with salt
" and "have a line with spaghetti
" have been preserved.
Not even the more relaxed intentions: "have the word salt
" and "have the word spaghetti
" have been preserved.
Solution: replace rather than change empty lines🔗
If we treat inserts into empty lines as a combination of a conditional erase and an insert, we can preserve the intentions of both users. This would give us e.g.:
salt
spaghetti
The conditional erase deletes the current line (= the line-break) where the insert is happening, but only if the current line is empty.
Depending on the implementation, the empty file (or the beginning/end of the file) might have to be special-cased as there is not necessarily a line-break to erase.
Also depending on the implementation, there might need to be modifications to how inserts and other edits affecting the conditionally erased line are handled. The conditionally erased line should probably be resurrected for most of these cases.
E.g. if the line was made empty by deleting some text and another user added more text in it while still seeing the text, the line should be re-added.
Let's say the initial state was
...whatever before
can of beans
...whatever after
u_0 changes this to
...whatever before
2x can of beans
...whatever after
Concurrently, u_1 deletes can of beans
and adds a new entry
...whatever before
bread
...whatever after
The final file state according to our rules should then be this (lines may be swapped):
...whatever before
2x
bread
...whatever after
This makes it obvious that there is some information missing, which a user can easily verify by looking at the file history.
Without our rule to restore the line-break of the line which originally contained can of beans
, we would for instance get:
...whatever before
2x bread
...whatever after
This would wrongly suggest that there was no conflict.