automerge / automerge-classic

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically.
http://automerge.org/
MIT License
14.75k stars 467 forks source link

Same change using Automerge.Text #484

Closed LiraNuna closed 2 years ago

LiraNuna commented 2 years ago

Hello,

When two clients make the same change to an Automerge.Text, namely replacing a single character, the character gets duplicated.

Example code:

const Automerge = require('automerge');

const base = Automerge.save(Automerge.change(Automerge.init(), document => {
    document.text = new Automerge.Text('hello');
}));

// hello => Hello
const c1 = Automerge.change(Automerge.load(base), document => {
    document.text.deleteAt(0, 1);
    document.text.insertAt(0, 'H');
});

// hello => Hello
const c2 = Automerge.change(Automerge.load(base), document => {
    document.text.deleteAt(0, 1);
    document.text.insertAt(0, 'H');
});

const merged = Automerge.merge(Automerge.merge(Automerge.load(base), c1), c2);
console.log(merged.text.toString());  // HHello

I'm wondering if this is me misunderstanding how to use Automerge, Automerge.Text or just general misunderstanding. Is there a better way to handle this kind of change?

ept commented 2 years ago

Hi @LiraNuna, this is the intended behaviour. If you want to replace a character, you can use document.text.set(index, char), which will not duplicate it. If two users set the same index to different characters, Automerge will pick one of them arbitrarily and ensure all users pick the same one. Is this what you're looking for?

LiraNuna commented 2 years ago

Thanks for the answer.

I don't think .set() is going to help my use case. You see, I'm working on a collaborative text editing software, and the removal and insertions are mirrored actions of what the user actually made using their keyboard. Since there's no way to "replace" a character atomically, the actions are "backspace + H" which translate to the deletion and insertion independently.

We also tried diffing the texts instead of relying on direct keyboard inputs however that generates the same primitives (removed characters and inserted characters).

Would you recommend a different approach?

ept commented 2 years ago

All collaborative text editors that I'm aware of have the same behaviour as Automerge. For example, I just tested Google Docs and it also duplicates the character in this situation.

If the algorithm did avoid duplicates in the situation you describe, how should it handle the situation where two users start by typing the same thing but then continue typing different things? For example, one user deletes h and inserts Hi, while the other deletes h and inserts Hello. Automerge will resolve this to either HiHello or to HelloHi. If you deduplicate the insertions of H as you suggest, the merged result will be something like Hiello or Helloi, which I think is a worse outcome than what Automerge does. For that reason I think it is not desirable to suppress the duplicated character.

LiraNuna commented 2 years ago

Thank you so much for the explanation!

pvh commented 2 years ago

@ept one alternative would be to consider a case like this a conflict and both highlight it as such and also only pick a single one. In this case, the outcome would be either Hello or Hi! It's not clear to me how we would actually accomplish this, but what the desired outcome would be is a separate question from the mechanism to achieve it.