[WebODF] Adding spec property "setOwnCursor" for Insert/Remove/Split ops

Friedrich W. H. Kossebau friedrich at kogmbh.com
Mon Jun 17 12:39:46 CEST 2013


Hi WebODFler,

I would like to add a new property to the specs of all insert/remove/split 
ops: setOwnCursor (or some better name). This property will tell if the cursor 
of the member which sends the operation should be put after the place/result 
of the op.
Currently this is only done iff the cursor is at the position of the op. But 
with real OT, where local operations are instantly applied and only then sent 
to other clients who might have to transform them on conflicts, the 
transformed operations and positions of cursors can be out-of-sync, breaking 
the current assumption with UI editing that cursors will always automatically 
placed behind the places of modification.

Example:
<text:p><cursor memberid="A"><cursor memberid="B"></text:p>
Both clients A and B type at the same time "a" and "b" and apply it locally, 
before sending to server/other client:
At A:
<text:p><cursor memberid="B">a<cursor memberid="A"></text:p>
Unsynced ops: [<insertText pos="0" text="a" memberId="A"/>]
At B:
<text:p><cursor memberid="A">b<cursor memberid="B"></text:p>
Unsynced ops: [<insertText pos="0" text="b" memberId="B"/>]
At server:
op stack: []
As this is a case for tiebreaking on conflicting operations, because both 
insert at the same position and it needs to be decided in which order the 
insertions will end up in the final document, we assume that
* priority is given to ops which are first on the server
* priority in this case means insertion will be placed before the other 
insertion
A is quicker with syncing with server:
At A:
<text:p><cursor memberid="B">a<cursor memberid="A"></text:p>
Unsynced ops: []
At server:
op stack: [<insertText pos="0" text="a" memberId="A"/>]
Now B tries to sync, but sees that there are conflicting ops, which it first 
has to resolve locally, by transforming its local unsynced op and the new on 
the server against each other, so the server op is based on the state of B and 
the unsynced op is based on the (virtual) state of the server:
T(<insertText pos="1" text="b" memberId="B"/>,
  <insertText pos="0" text="a" memberId="A"/> )
->
(<insertText pos="1" text="b" memberId="B"/>,
 <insertText pos="0" text="a" memberId="A"/> )
given the tiebreaking rules above.
After transformation B then applies the transformed op from the server and 
sends its own transformed unsynced op to the server
At B:
<text:p>a<cursor memberid="A">b<cursor memberid="B"></text:p>
Unsynced ops: []
At server:
op stack: [<insertText pos="0" text="a" memberId="A"/>,
           <insertText pos="1" text="b" memberId="B"/>]
Now A syncs again with the server, fetching the new op.
And here we are with the problem: At A the cursor of B is still at the 
position 0, while the operation says that B inserts at position 1. Still we 
want to have the cursor of B after the op to be at position 3 (not only for 
convergence of the 2 clients).

I would like to solve that by allowing ops to optionally take the cursor from 
whereever it was and just move it behind the result of the operation, instead 
of always only moving it if it is at the position of the operation.
This would also allow to explicitely do operations at the current cursor 
position without moving the cursor, which currently is not possible.

Operation creators would specify the cursor movement by setting the new 
property "setOwnCursor" to "false" or "true". It would be added to all 
operations where the text structure is changed.

Any blockers, counterproposals, comments, icecoffee recipes?

Cheers
Friedrich
-- 
Friedrich W. H. Kossebau // KO GmbH
http://kogmbh.com/legal/


More information about the WebODF mailing list