Yep, you're absolutely right about why it happens, although its not
that easy to fix.

Sometimes there actually _is_ information in the order of inserts and deletes.

Say the document looks like this:
  "hello"

.. and I do two ops: I insert 'goodbye' before the 'hello' and delete
the 'hello':
op1: [insert:'goodbye', skip:5]
op2: [skip:7, delete:'hello']
-> "goodbye"
While you insert characters after the hello:
[skip:5, insert:"mum"]
-> "hellomum"

If you compose my ops together, they become [insert:'goodbye',
delete:'hello']. And they have to be that way around. If they weren't,
its possible for your 'mum' to appear before my 'goodbye'. .... which
just doesn't make sense.

If you swap deletes and inserts around in ambiguous situations in the
composer (like the example), you don't remove the problem. You just
need slightly different ops to demonstrate it.

-J


On Sun, Mar 6, 2011 at 9:14 AM, Wim Looman <ghostundersc...@gmail.com> wrote:
> I believe this is caused by the Composer and Transformer working slightly 
> differently.
>
> Basically by placing the insert after the delete the Composer is encoding 
> non-existent information about the location of the insert, namely that it was 
> inserted after the location of the delete.
>
> Whereas the Transformer receiving `c1` then `c2` will be working on the 
> assumption that the 'c' was inserted to the left of the delete location.
>
> So based on this one example I think having the Composer place inserts that 
> occur after deletes to the left of the deletes would work, however I think 
> this could break depending on if the composition is happening on the server 
> side or the client side.
> --
> Wim Looman
> ghostundersc...@gmail.com
>
>
> On 5/03/2011, at 9:43 PM, Joseph Gentle wrote:
>
>> On Sat, Mar 5, 2011 at 6:45 PM, Joseph Gentle <jose...@gmail.com> wrote:
>>> s = insert: 's', skip: 1
>>> c1 = delete: 'x'
>>> c2 = insert: 'c'
>>>
>>>
>>>    // Make println() usable.
>>>    DocOpScrub.setShouldScrubByDefault(false);
>>>
>>>    DocOp s = new DocOpBuilder().characters("s").retain(1).build();
>>>
>>>    DocOp c1 = new DocOpBuilder().deleteCharacters("x").build();
>>>    DocOp c2 = new DocOpBuilder().characters("c").build();
>>>
>>>    DocOp cc = Composer.compose(c1, c2);
>>>
>>>    // s_ = s T c1
>>>    DocOp s_ = Transformer.transform(c1, s).serverOp();
>>>    // s__ = s T c1 T c2
>>>    DocOp s__ = Transformer.transform(c2, s_).serverOp();
>>>
>>>    // cc = c1 + c2
>>>    System.out.println("cc: " + cc);
>>>    // scc_ = s T (c1 + c2)
>>>    DocOp scc_ = Transformer.transform(cc, s).serverOp();
>>>
>>>    // s__ and scc_ are different!
>>>    System.out.println("s__: " + s__);
>>>    System.out.println("scc_: " + scc_);
>>>    System.out.println();
>>>
>>>    // ... And not just different syntactically. They're different 
>>> semantically.
>>>    DocOp doc1 = Composer.compose(ImmutableList.of(new
>>> DocOpBuilder().characters("x").build(), c1, c2, s__));
>>>    System.out.println("doc1: " + doc1);
>>>
>>>    DocOp doc2 = Composer.compose(ImmutableList.of(new
>>> DocOpBuilder().characters("x").build(), cc, scc_));
>>>    System.out.println("doc2: " + doc2);
>>>
>>
>> How rude of me!
>>
>> Output:
>>
>> cc: [--"x"; ++"c"; ]
>>
>> s__: [__1; ++"s"; ]
>> scc_: [++"s"; __1; ]
>> ^----- You would expect these last two to be equivalent
>>
>> doc1: [++"cs"; ]
>> doc2: [++"sc"; ]
>> ^----- .... and these to be identical.
>
>

Reply via email to